Phi
CodeHighlighted
@dicehub/phiv1.0.0-beta.1

CodeHighlighted

Shiki-powered syntax highlighting with VS Code-quality highlighting, theming, and lazy loading.

const greeting = "Hello, World!";
console.log(greeting);
<script setup>
import { ShikiProvider, CodeHighlighted } from "@dicehub/phi/code";
</script>

<template>
  <ShikiProvider engine="javascript" :languages="['typescript']">
    <CodeHighlighted
      code="const greeting = 'Hello, World!';\nconsole.log(greeting);"
      lang="typescript"
    />
  </ShikiProvider>
</template>

Overview

CodeHighlighted uses Shiki for TextMate grammar highlighting. It lives in@dicehub/phi/code so apps that do not render code blocks avoid the Shiki bundle.

Installation

Import CodeHighlighted from the code entrypoint, not from the main package.

import { ShikiProvider, CodeHighlighted } from "@dicehub/phi/code";

Important: Do not import CodeHighlighted from the main@dicehub/phi entrypoint. Use @dicehub/phi/codeso apps without code blocks do not download Shiki.

Basic Usage

Wrap your app with ShikiProvider once. Nested CodeHighlighted instances share the same Shiki instance, and equivalent language arrays reuse it across parent rerenders.

<script setup>
import { ShikiProvider, CodeHighlighted } from "@dicehub/phi/code";
</script>

<template>
  <ShikiProvider engine="javascript" :languages="['typescript', 'bash', 'json']">
    <CodeHighlighted code="const x = 1;" lang="typescript" />
  </ShikiProvider>
</template>

Examples

Languages

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();
}
<CodeHighlighted
  :code="source"
  lang="typescript"
/>

Vue

<script setup lang="ts">
import { ref } from "vue";

const count = ref(0);
</script>

<template>
  <button @click="count++">Count: {{ count }}</button>
</template>
<CodeHighlighted
  :code="source"
  lang="html"
/>

Bash / Shell

# Install Phi
pnpm add @dicehub/phi@beta

# Start development server
pnpm dev
<CodeHighlighted code="pnpm add @dicehub/phi@beta" lang="bash" />

JSON

{
  "name": "@dicehub/phi",
  "version": "0.0.0",
  "dependencies": {
    "vue": "^3.5.0",
    "shiki": "^4.0.2"
  }
}
<CodeHighlighted :code="packageJson" lang="json" />

CSS

.button {
  background: var(--phi-accent);
  border-radius: 0.5rem;
  padding: 0.5rem 1rem;
}

.button:hover {
  background: var(--phi-accent-hover);
}
<CodeHighlighted :code="css" lang="css" />

Highlight Lines

Emphasize specific lines with highlightLines.

function processData(items: string[]) {
  // Filter out empty items
  const filtered = items.filter(Boolean);

  // Transform to uppercase
  const transformed = filtered.map((item) => item.toUpperCase());

  return transformed.toSorted();
}
<CodeHighlighted
  :code="source"
  lang="typescript"
  :highlight-lines="[5, 6]"
/>

Custom Highlight Color

Override --phi-code-highlight-bg when a local highlight color is needed.

function processData(items: string[]) {
  // Filter out empty items
  const filtered = items.filter(Boolean);

  // Transform to uppercase
  const transformed = filtered.map((item) => item.toUpperCase());

  return transformed.toSorted();
}
--phi-code-highlight-bg: rgba(0, 0, 0, 0.05)

Line Numbers

import { ref, onMounted, onUnmounted } from "vue";

export function useWindowSize() {
  const size = ref({ width: 0, height: 0 });

  const handleResize = () => {
    size.value = {
      width: window.innerWidth,
      height: window.innerHeight,
    };
  };

  onMounted(() => {
    handleResize();
    window.addEventListener("resize", handleResize);
  });

  onUnmounted(() => window.removeEventListener("resize", handleResize));

  return size;
}
<CodeHighlighted
  :code="source"
  lang="typescript"
  show-line-numbers
/>

Copy Button

pnpm add @dicehub/phi@beta
<CodeHighlighted
  code="pnpm add @dicehub/phi@beta"
  lang="bash"
  show-copy-button
/>
<CodeHighlighted
  :code="source"
  lang="html"
  show-copy-button
  :highlight-lines="[5, 6, 7, 8, 9]"
/>

Plain

Use variant="plain" when the code sits inside another surface and must not draw its own frame.

import { ShikiProvider, CodeHighlighted } from "@dicehub/phi/code";

<template>
  <ShikiProvider engine="javascript" :languages="['typescript', 'bash', 'json']">
    <CodeHighlighted
      :code="code"
      lang="typescript"
      show-copy-button
      :highlight-lines="[5, 6]"
    />
  </ShikiProvider>
</template>
<CodeHighlighted
  :code="source"
  lang="html"
  show-copy-button
  :highlight-lines="[5, 6, 7, 8, 9]"
  variant="plain"
/>

Shared Provider

Multiple code blocks can share the same provider and highlighter instance.

const config = { theme: 'dark' };
pnpm run build
{ "success": true }
<ShikiProvider engine="javascript" :languages="['typescript', 'bash', 'json']">
  <CodeHighlighted code="const config = { theme: 'dark' };" lang="typescript" />
  <CodeHighlighted code="pnpm run build" lang="bash" />
  <CodeHighlighted code='{ "success": true }' lang="json" />
</ShikiProvider>

Themes

CodeHighlighted uses hardcoded themes for consistent styling across Phi applications.

  • Light mode: github-light
  • Dark mode: vesper

Theme customization is intentionally not part of the public API.

Server-Side Usage

Use the server utilities in SSR or static rendering when you want highlighted HTML without client-side Shiki.

One-off Highlighting

import { highlightCode } from "@dicehub/phi/code/server";

const html = await highlightCode("const x = 1;", "typescript");

Reusable Highlighter

import { createServerHighlighter } from "@dicehub/phi/code/server";

const highlighter = await createServerHighlighter({
  languages: ["typescript", "bash", "json"],
});

const first = highlighter.highlight(source, "typescript");
const second = highlighter.highlight(command, "bash");

highlighter.dispose();

Custom Composable

Use useShikiHighlighter for custom renderers inside a provider.

<script setup>
import { useShikiHighlighter } from "@dicehub/phi/code";

const { highlight, isLoading, isReady, error } = useShikiHighlighter();
</script>

Internationalization

CodeHighlighted only owns the copy button text. Set translated labels on ShikiProvider, or override them on one CodeHighlighted instance for another locale.

<script setup>
import { ShikiProvider, CodeHighlighted } from "@dicehub/phi/code";
</script>

<template>
  <ShikiProvider
    engine="javascript"
    :languages="['typescript', 'bash']"
    :labels="{ copy: 'Copier', copied: 'Copié !' }"
  >
    <CodeHighlighted
      code="const status = 'ready';"
      lang="typescript"
      show-copy-button
    />
    <CodeHighlighted
      code="pnpm add @dicehub/phi@beta"
      lang="bash"
      show-copy-button
      :labels="{ copy: 'Befehl kopieren', copied: 'Befehl kopiert' }"
    />
  </ShikiProvider>
</template>

Framework Integration

Vue Router

<script setup>
import { ShikiProvider } from "@dicehub/phi/code";
</script>

<template>
  <ShikiProvider engine="javascript" :languages="['typescript', 'bash', 'json']">
    <RouterView />
  </ShikiProvider>
</template>

Astro Static

For static sites, server-side highlighting avoids client JavaScript for code blocks.

---
import { highlightCode } from "@dicehub/phi/code/server";

const html = await highlightCode(Astro.props.code, Astro.props.lang);
---

<div class="code-block" set:html={html} />

Plain Code

Code and CodeBlock render lightweight monospace snippets from@dicehub/phi/components/code. Use CodeHighlighted when you need syntax highlighting.

<script setup>
import { CodeBlock } from "@dicehub/phi/components/code";
import { ShikiProvider, CodeHighlighted } from "@dicehub/phi/code";
</script>

<template>
  <!-- Plain monospace code, no syntax highlighting -->
  <CodeBlock code="const x = 1;" lang="ts" />

  <!-- Highlighted code -->
  <ShikiProvider engine="javascript" :languages="['typescript']">
    <CodeHighlighted code="const x = 1;" lang="typescript" />
  </ShikiProvider>
</template>

API Reference

ShikiProvider

PropTypeRequiredDescription
engine"javascript" | "wasm"NoHighlighting engine. Defaults to javascript.
languagesstring[]YesLanguages to load. Aliases such as ts, js, sh, and py are normalized.
labels{ copy?: string; copied?: string }NoLocalized labels for copy controls.

CodeHighlighted

PropTypeRequiredDescription
codestringYesSource code to display.
langstringYesLanguage identifier. It must be present in the provider languages list.
showLineNumbersbooleanNoDisplays a line-number gutter.
highlightLinesnumber[]NoOne-indexed lines to emphasize.
showCopyButtonbooleanNoAdds a copy-to-clipboard button.
labels{ copy?: string; copied?: string }NoOverrides provider labels for this instance.
variant"default" | "plain"NoPlain removes the frame, background, radius, and code padding, and keeps highlighted lines inside the content width.

useShikiHighlighter

PropertyTypeDescription
highlight(code, lang) => string | nullReturns highlighted HTML, or null when unavailable.
isLoadingRef<boolean>True while Shiki is loading.
isReadyComputedRef<boolean>True when highlight() is safe to call.
errorShallowRef<Error | null>Error if Shiki initialization failed.
labelsComputedRef<Required<CodeHighlightedLabels>>Resolved copy button labels.