DDocsKit/Docs

Type to search. Run pnpm build to enable in dev mode.

↑↓navigateopenescclose

CodeBlock

DocsKit renders syntax-highlighted code blocks using Shiki, a server-side highlighter that produces accurate, token-level coloring for over 100 languages. Code blocks support an optional filename tab and include a one-click copy button.

Basic usage

Write fenced code blocks in your MDX exactly as you would in standard Markdown. The language identifier after the opening fence tells Shiki which grammar to apply:

```typescript
function greet(name: string): string {
  return `Hello, ${name}!`;
}
```

Renders as:

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

Filename tabs

Add a filename tab by including filename="..." in the opening fence. This is particularly useful in tutorials where readers need to know which file to edit:

```ts filename="src/lib/client.ts"
import { createClient } from "@acme/sdk";
 
export const client = createClient({
  apiKey: process.env.ACME_API_KEY!,
  baseUrl: "https://api.acme.dev",
});
```

Renders as:

import { createClient } from "@acme/sdk";
 
export const client = createClient({
  apiKey: process.env.ACME_API_KEY!,
  baseUrl: "https://api.acme.dev",
});

The filename appears in a tab above the code. The tab is purely decorative - it does not make the block interactive or editable.

Copy button

Every code block includes a copy button in the top-right corner. It copies the raw code text (without the filename or syntax highlighting markup) to the clipboard. The button fades to visible on hover and shows a checkmark briefly after a successful copy.

The copy button is rendered client-side and does not appear in the static HTML - it's injected via a small script after page load. This means it works correctly in static export mode.

Supported languages

Shiki supports 100+ languages. The most commonly used ones in technical documentation:

LanguageIdentifierExample use
TypeScriptts or typescriptSDK examples, type definitions
JavaScriptjs or javascriptBrowser scripts, Node.js
TSXtsxReact components with TypeScript
JSXjsxReact components with JavaScript
Bash / Shellbash or shCLI commands, scripts
JSONjsonConfiguration files, API responses
CSScssStylesheet examples
HTMLhtmlMarkup examples
MDXmdxDocsKit content examples
Pythonpython or pyBackend examples
GogoServer-side examples
RustrustSystems programming examples
SQLsqlDatabase queries
YAMLyaml or ymlCI/CD config, Docker Compose

If you use a language identifier that Shiki doesn't recognize, it will render the block as plain text without an error.

Use bash rather than shell or sh for terminal commands - Shiki's bash grammar handles common patterns like flags, pipes, and environment variables the best.

Real-world example

Here's a more complete example showing a TypeScript file with multiple imports, types, and async functions - the kind of snippet that appears in real API integration guides:

import { type IncomingMessage, type ServerResponse } from "http";
import crypto from "crypto";
 
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!;
 
interface WebhookPayload {
  event: "project.created" | "project.deleted" | "deploy.succeeded";
  timestamp: number;
  data: Record<string, unknown>;
}
 
function verifySignature(body: string, signature: string): boolean {
  const expected = crypto
    .createHmac("sha256", WEBHOOK_SECRET)
    .update(body, "utf8")
    .digest("hex");
 
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(`sha256=${expected}`)
  );
}
 
export async function handleWebhook(
  req: IncomingMessage,
  res: ServerResponse
): Promise<void> {
  const signature = req.headers["x-acme-signature"] as string;
  const body = await readBody(req);
 
  if (!verifySignature(body, signature)) {
    res.writeHead(401);
    res.end("Unauthorized");
    return;
  }
 
  const payload: WebhookPayload = JSON.parse(body);
  console.log(`Received event: ${payload.event}`);
 
  res.writeHead(200);
  res.end("OK");
}
 
async function readBody(req: IncomingMessage): Promise<string> {
  return new Promise((resolve, reject) => {
    const chunks: Buffer[] = [];
    req.on("data", (chunk) => chunks.push(chunk));
    req.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
    req.on("error", reject);
  });
}

Changing the syntax theme

The default theme is vesper. To switch themes, update the theme option in src/lib/mdx.ts. See Configuration Options - Syntax highlighting themes for the full list of available themes.