Syntax Highlighting

eziwiki uses Shiki for beautiful, accurate syntax highlighting.

Why Shiki?

  • Accurate β€” uses the same TextMate grammars as VS Code
  • Beautiful β€” the highlighting you already know from your editor
  • Zero client cost β€” code is highlighted during the build, so no highlighter is sent to the browser
  • Both themes at once β€” light and dark are emitted as CSS variables, so switching theme never flashes or re-highlights

Supported Languages

Shiki bundles grammars for over a hundred languages β€” JavaScript, TypeScript, Python, Go, Rust, C, C++, C#, Java, Ruby, PHP, SQL, GraphQL, HTML, CSS, YAML, TOML, Bash, and so on.

Only what you use is loaded

Loading all of them costs about twenty seconds before the first page renders, so eziwiki scans your content for the languages it actually contains and loads only those, plus a handful of common defaults. This site loads sixteen grammars and initialises in well under a second.

The practical effect: write a fence in any supported language and it just works β€” the next build picks it up. A fence whose language Shiki does not recognise renders as plain text rather than failing the build.

Usage

Basic Code Block

Use triple backticks with a language identifier:

markdown
```javascript
function greet(name) {
  return `Hello, ${name}!`;
}
```

Result:

javascript
function greet(name) {
  return `Hello, ${name}!`;
}

TypeScript Example

markdown
```typescript
interface User {
  id: string;
  name: string;
  email: string;
}

async function fetchUser(id: string): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}
```
typescript
interface User {
  id: string;
  name: string;
  email: string;
}

async function fetchUser(id: string): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

Python Example

markdown
```python
def calculate_fibonacci(n: int) -> list[int]:
    """Generate Fibonacci sequence."""
    if n <= 0:
        return []

    fib = [0, 1]
    for i in range(2, n):
        fib.append(fib[i-1] + fib[i-2])

    return fib
```
python
def calculate_fibonacci(n: int) -> list[int]:
    """Generate Fibonacci sequence."""
    if n <= 0:
        return []

    fib = [0, 1]
    for i in range(2, n):
        fib.append(fib[i-1] + fib[i-2])

    return fib

Configuration

Change Theme

Edit lib/markdown/highlighter.ts:

typescript
import { getHighlighter } from 'shiki';

const highlighter = await getHighlighter({
  themes: ['github-light', 'github-dark'],  // Change themes here
  langs: ['javascript', 'typescript', ...],
});

Available Themes

Popular themes:

  • github-light, github-dark (default)
  • nord
  • dracula
  • monokai
  • one-dark-pro
  • material-theme
  • solarized-light, solarized-dark

See all themes.

Add Languages

Add more languages to support:

typescript
const highlighter = await getHighlighter({
  themes: ['github-light', 'github-dark'],
  langs: [
    'javascript',
    'typescript',
    'python',
    'rust', // Add Rust
    'kotlin', // Add Kotlin
    'swift', // Add Swift
  ],
});

Dark Mode Support

Code blocks automatically adapt to the theme:

typescript
// Light mode: github-light theme
// Dark mode: github-dark theme

const html = highlighter.codeToHtml(code, {
  lang: 'javascript',
  theme: isDark ? 'github-dark' : 'github-light',
});

Inline Code

Inline code uses a simple monospace style:

markdown
Use `const` instead of `var` in JavaScript.

Use const instead of var in JavaScript.

Line Numbers

To add line numbers, modify the highlighter configuration:

typescript
const html = highlighter.codeToHtml(code, {
  lang: 'javascript',
  theme: 'github-light',
  lineNumbers: true, // Enable line numbers
});

Line Highlighting

Highlight specific lines:

typescript
const html = highlighter.codeToHtml(code, {
  lang: 'javascript',
  theme: 'github-light',
  lineOptions: [
    { line: 3, classes: ['highlighted'] },
    { line: 5, classes: ['highlighted'] },
  ],
});

Copy Button

Add a copy button to code blocks:

typescript
'use client';

import { useState } from 'react';
import { Copy, Check } from 'lucide-react';

export function CodeBlock({ code, lang }: { code: string; lang: string }) {
  const [copied, setCopied] = useState(false);

  const copyCode = async () => {
    await navigator.clipboard.writeText(code);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  return (
    <div className="relative">
      <button
        onClick={copyCode}
        className="absolute top-2 right-2 p-2 rounded bg-gray-700 hover:bg-gray-600"
      >
        {copied ? <Check size={16} /> : <Copy size={16} />}
      </button>
      <pre>
        <code>{code}</code>
      </pre>
    </div>
  );
}

Language Detection

If no language is specified, Shiki tries to detect it:

markdown
```
function hello() {
  console.log('Hello!');
}
```

But it's better to always specify the language:

markdown
```javascript
function hello() {
  console.log('Hello!');
}
```

Performance

Build-Time Rendering

Code blocks are highlighted at build time, not runtime:

typescript
// During build
const html = highlighter.codeToHtml(code, { lang, theme });

// Served as static HTML
<div dangerouslySetInnerHTML={{ __html: html }} />

This means:

  • Fast loading - No client-side processing
  • Small bundle - No syntax highlighting library in browser
  • SEO friendly - Fully rendered HTML

Bundle Size

Shiki only runs at build time, so it doesn't increase your client bundle size.

Best Practices

Always Specify Language

markdown
βœ… Good:

```javascript
const x = 10;
```

❌ Bad:

```
const x = 10;
```

Use Proper Indentation

markdown
βœ… Good:

```javascript
function example() {
  if (true) {
    console.log('Properly indented');
  }
}
```

❌ Bad:

```javascript
function example() {
  if (true) {
    console.log('Bad indentation');
  }
}
```

Add Comments

markdown
```javascript
// Initialize user data
const user = {
  name: 'Alice',
  email: 'alice@example.com',
};

// Send welcome email
sendEmail(user.email, 'Welcome!');
```

Keep Examples Focused

markdown
βœ… Good - focused example:

```javascript
// Calculate total
const total = items.reduce((sum, item) => sum + item.price, 0);
```

❌ Bad - too much code:

```javascript
// 100 lines of unrelated code...
```

Troubleshooting

Language Not Recognized

If a language isn't highlighted:

  1. Check the language name is correct
  2. Add it to langs array in highlighter config
  3. See supported languages

Theme Not Working

If theme doesn't apply:

  1. Check theme name is correct
  2. Add it to themes array in highlighter config
  3. Rebuild the site: npm run build

Code Not Highlighting

If code blocks aren't highlighted:

  1. Check triple backticks are correct
  2. Verify language identifier is specified
  3. Check for syntax errors in code
  4. Rebuild the site

Examples

Diff Highlighting

Show code changes:

markdown
```diff
- const oldValue = 10;
+ const newValue = 20;
```

Shell Commands

markdown
```bash
# Install dependencies
npm install

# Start dev server
npm run dev
```

Configuration Files

markdown
```json
{
  "name": "my-project",
  "version": "1.0.0",
  "scripts": {
    "dev": "next dev",
    "build": "next build"
  }
}
```

Next Steps