Code Block

Learn how to integrate Shiki, a beautiful yet powerful syntax highlighter.

Shiki Documentation

Installation

Install Shiki with your preferred package manager.

bash
npm install -D shiki

Next.js Integration

Shiki provides official documentation for integrating into Next.js. This guide will follow this outline.

Create a Component

A reusable component should suffice in most projects. Follow the steps below.

React

  1. Implement a new <CodeBlock> component in /src/components/code-block.tsx.
  2. Implement several variations of our <CodeBlock> component in any route page.tsx.

Svelte

  1. Implement a new <CodeBlock> component in /src/lib/components/code-block.svelte.
  2. Implement several variations of our <CodeBlock> component in any SvelteKit route +page.svelte.

React

tsx
import { createHighlighterCore } from 'shiki/core';
import { createJavaScriptRegexEngine } from 'shiki/engine/javascript';

const highlighter = await createHighlighterCore({
	langs: [import('@shikijs/langs/bash'), import('@shikijs/langs/css'), import('@shikijs/langs/html'), import('@shikijs/langs/javascript')],
	themes: [import('@shikijs/themes/github-dark')],
	engine: createJavaScriptRegexEngine(),
});

interface CodeBlockProps {
	code: Parameters<typeof highlighter.codeToHtml>[0];
	lang?: Parameters<typeof highlighter.codeToHtml>[1]['lang'];
	// Base Style Props
	base?: string;
	background?: string;
	rounded?: string;
	shadow?: string;
	classes?: string;
	// Pre Style Props
	preBase?: string;
	prePadding?: string;
	preClasses?: string;
}

export default function CodeBlock({
	code,
	lang = 'txt',
	// Base Style Props
	base = 'overflow-hidden',
	background = 'bg-neutral-950',
	rounded = 'rounded-container',
	shadow = '',
	classes = '',
	// Pre Style Props
	preBase = '',
	prePadding = '',
	preClasses = '',
}: CodeBlockProps) {
	const html = highlighter.codeToHtml(code, {
		lang,
		theme: 'github-dark',
	});
	return (
		<div
			dangerouslySetInnerHTML={{ __html: html }}
			className={`${base} ${background} ${rounded} ${shadow} ${classes} ${preBase} ${prePadding} ${preClasses}`}
		></div>
	);
}
tsx
import CodeBlock from './code-block';

export default function Page() {
	return (
		<div className="p-10 space-y-4">
			<CodeBlock code="npx sv create my-skeleton-app" lang="bash" />
			<CodeBlock code='<div class="bg-green-500"' lang="html" />
			<CodeBlock code=".foobar { background: green; }" lang="css" />
			<CodeBlock code="const foot = 'bar';" lang="js" />
		</div>
	);
}

Svelte

svelte
<script lang="ts" module>
	import { createHighlighterCore } from 'shiki/core';
	import { createJavaScriptRegexEngine } from 'shiki/engine/javascript';

	const highlighter = await createHighlighterCore({
		langs: [
			import('@shikijs/langs/bash'),
			import('@shikijs/langs/css'),
			import('@shikijs/langs/html'),
			import('@shikijs/langs/javascript'),
		],
		themes: [import('@shikijs/themes/github-dark')],
		engine: createJavaScriptRegexEngine(),
	});

	interface CodeBlockProps {
		code: Parameters<typeof highlighter.codeToHtml>[0];
		lang?: Parameters<typeof highlighter.codeToHtml>[1]['lang'];
		// Base Style Props
		base?: string;
		background?: string;
		rounded?: string;
		shadow?: string;
		classes?: string;
		// Pre Style Props
		preBase?: string;
		prePadding?: string;
		preClasses?: string;
	}
</script>

<script lang="ts">
	const {
		code = '',
		lang = 'txt',
		// Base Style Props
		base = ' overflow-hidden',
		rounded = 'rounded-container',
		shadow = '',
		classes = '',
		// Pre Style Props
		preBase = '',
		prePadding = '',
		preClasses = '',
	}: CodeBlockProps = $props();

	const generatedHtml = highlighter.codeToHtml(code, {
		lang,
		theme: 'github-dark',
	});
</script>

<div class="{base} {rounded} {shadow} {classes} {preBase} {prePadding} {preClasses}">
	<!-- Output Shiki's Generated HTML -->
	{@html generatedHtml}
</div>
svelte
<script lang="ts">
	import CodeBlock from './code-block.svelte';
</script>

<div class="p-10 space-y-4">
	<CodeBlock code="npx sv create my-skeleton-app" lang="bash" />
	<CodeBlock code="<div class=&quot;bg-green-500&quot;" lang="html" />
	<CodeBlock code=".foobar &#123; background: green; &#125;" lang="css" />
	<CodeBlock code="const foot = 'bar';" lang="js" />
</div>

A few things of note about this component:

  • You will need to import and configure any number of Shiki themes.
  • You will need to import and configure any number of supported languages.
  • The component has been implemented using Skeleton's component style guidelines.
  • This provides a number of style props for easy customization via Skeleton's standard conventions.
  • The component exposes code, lang, and theme properties to configure on-the-fly.
  • The Code Block <pre> tag is auto-generated by Shiki; target utility classes with: [&>pre]:myClassHere.

Programmatic Usage

Warning

This use case falls outside the scope of Skeleton. The following is provided merely as guidance.

In some cases you may not have direct access to the source code, such as content from a blog posts or CMS pages. In fact the code may even come pre-baked with surrounding <pre> or <code> elements. For this, you'll need to follow the general steps below. Specific implementation may differ based on your app and framework.

  1. Query all <pre> or <code> blocks using Javascript tools like document.querySelectorAll(). Be as specific as possible.
  2. Ensure you have a clean instance of the source code itself, with no extra markup injected within.
  3. Use Shiki's codeToHtml feature to parse the code as styled HTML markup.
  4. Then append each instance of the code blocks in your DOM.

Custom Themes

Shiki provides support for generating a custom highlighter theme:

Shiki theme values can be defined using Skeleton custom theme properties, such as rgba(--color-primary-500).

Accessibility

See Salma Alam-Naylor's guidelines for creating accessible code blocks that meet WGAC standards.