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
<CodeHighlighted
code="pnpm add @dicehub/phi@beta"
lang="bash"
show-copy-button
/>Full Featured
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]"
/>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
| Prop | Type | Required | Description |
|---|---|---|---|
engine | "javascript" | "wasm" | No | Highlighting engine. Defaults to javascript. |
languages | string[] | Yes | Languages to load. Aliases such as ts, js, sh, and py are normalized. |
labels | { copy?: string; copied?: string } | No | Localized labels for copy controls. |
CodeHighlighted
| Prop | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Source code to display. |
lang | string | Yes | Language identifier. It must be present in the provider languages list. |
showLineNumbers | boolean | No | Displays a line-number gutter. |
highlightLines | number[] | No | One-indexed lines to emphasize. |
showCopyButton | boolean | No | Adds a copy-to-clipboard button. |
labels | { copy?: string; copied?: string } | No | Overrides provider labels for this instance. |
variant | "default" | "plain" | No | Plain removes the frame, background, radius, and code padding, and keeps highlighted lines inside the content width. |
useShikiHighlighter
| Property | Type | Description |
|---|---|---|
highlight | (code, lang) => string | null | Returns highlighted HTML, or null when unavailable. |
isLoading | Ref<boolean> | True while Shiki is loading. |
isReady | ComputedRef<boolean> | True when highlight() is safe to call. |
error | ShallowRef<Error | null> | Error if Shiki initialization failed. |
labels | ComputedRef<Required<CodeHighlightedLabels>> | Resolved copy button labels. |