The CodeHighlighter component provides a powerful and flexible way to display interactive code examples with syntax highlighting, multiple variants, and live previews.
It supports both static code blocks and interactive demos with component previews.
The component uses the Props Context Layering pattern to work seamlessly across server and client boundaries, allowing progressive enhancement from static code to interactive demos.
Let's start with the fundamentals. CodeHighlighter can display simple code blocks with syntax highlighting, perfect for documentation where you just need to show code examples:
We can use it to highlight code snippets in various languages, like JavaScript, TypeScript, or even CSS.
console.log('Hello, world!');import * as React from 'react';
import { Code } from './CodeBlock';
export function BasicCode() {
return <Code fileName="hello.js">{`console.log('Hello, world!');`}</Code>;
}
But CodeHighlighter really shines when you want to combine working React components with their source code. This creates an interactive experience where users can see both the rendered output and the implementation.
import * as React from 'react';
import { Checkbox } from '@/components/Checkbox';
export function CheckboxBasic() {
return <Checkbox defaultChecked />;
}
import { CheckboxBasic } from './CheckboxBasic';
import { createDemo } from './createDemo';
export const DemoCheckboxBasic = createDemo(import.meta.url, CheckboxBasic);
Tip
New to the overall approach? See the broader Built Factories Pattern for why factories use
import.meta.url, how variants are derived, and the server vs client split.
Note
Before you can use
createDemo()in your demos, you must first create this factory function in your repo (see theabstractCreateDemodocs for details).
To unlock CodeHighlighter's full potential—especially build-time optimization—you'll need to use the factory pattern. This isn't just a convention; it's required for precomputation to work.
The factory pattern uses createDemo() to structure your demos in a way that the webpack loader can process and cache at build time.
You implement createDemo within your app using abstractCreateDemo to define the structure and behavior of your demos.
All demos must use this structure in an index.ts file:
// app/components/checkbox/demos/basic/index.ts
import { createDemo } from '../createDemo';
import { Checkbox } from './Checkbox';
export const DemoCheckboxBasic = createDemo(import.meta.url, Checkbox);
For multiple variants:
// app/components/checkbox/demos/basic/index.ts
import { createDemoWithVariants } from '../createDemo';
import { Checkbox as CssModules } from './css-modules/Checkbox';
import { Checkbox as Tailwind } from './tailwind/Checkbox';
export const DemoCheckboxBasic = createDemoWithVariants(import.meta.url, { CssModules, Tailwind });
When using the factory pattern with precomputation, organize your demo files like this:
app/components/checkbox/demos/
basic/
index.ts # Factory file (processed by webpack loader)
Checkbox.tsx # Component implementation
Checkbox.module.css # Dependencies (auto-loaded)
This follows Next.js' file based routing conventions, similar to how app/components/checkbox/page.tsx is built as a "page",
app/components/checkbox/demos/basic/index.ts is built as a "demo".
This also allows demo components to follow internal naming conventions, like CheckboxBasic.tsx that has a named export of CheckboxBasic, while the demo itself can be loaded by importing ./demos/basic (without needing to specify the index file).
It allows imports to remain the same when variants are added, e.g. import { DemoCheckboxBasic } from './demos/basic' will still work even if the demo is split into multiple variants.
app/components/checkbox/demos/
basic/
index.ts # Factory file (processed by webpack loader)
tailwind/
index.ts # Simple re-export of the implementation (optional)
Checkbox.tsx # Component implementation
css-modules/
index.ts # Simple re-export of the implementation (optional)
Checkbox.tsx # Component implementation
Checkbox.module.css # Dependencies (auto-loaded)
The precomputation is handled by a webpack loader that processes demo files at build time, enabling caching by the contents of the demo index file and all of its dependencies.
Note
For detailed information about the webpack loader implementation, configuration, and advanced usage, see the Precompute Loader Documentation.
Tip
Client-Side Implementation: For client-side loading, highlighting, and transforms, see the Code Provider Documentation which provides detailed examples of runtime processing.
Important: Precomputation only works:
./app/**/demos/*/index.ts)createDemo()CodeHighlighter usageOnce you've mastered the basics, CodeHighlighter offers several powerful features for more sophisticated use cases.
Sometimes you want to show different approaches to the same problem: CSS Modules versus Tailwind, Hooks or Blocks pattern, or different implementation strategies.
CodeHighlighter makes this seamless with variant switching.
Your components can even appear differently, but we recommend keeping them as similar as possible to avoid confusion.
import * as React from 'react';
import { Checkbox } from '@/components/Checkbox';
import styles from './CheckboxRed.module.css';
export function CheckboxRed() {
return <Checkbox defaultChecked className={styles.root} />;
}
import { CheckboxRed as CssModules } from './css-modules/CheckboxRed';
import { CheckboxRed as Tailwind } from './tailwind/CheckboxRed';
import { createDemoWithVariants } from './createDemo';
export const DemoCheckboxRed = createDemoWithVariants(import.meta.url, { CssModules, Tailwind });
One of CodeHighlighter's most powerful features is automatic code transformation. Write your examples in TypeScript, and users can instantly see the JavaScript equivalent:
const x: number = 1;
interface Props { name: string; }import * as React from 'react';
import { Code } from './Code';
export function BasicCode() {
return (
<Code fileName="example.ts">{`const x: number = 1;\ninterface Props { name: string; }`}</Code>
);
}
For pages with many code examples, you can defer syntax highlighting until the browser is idle, keeping your initial page loads fast:
console.log('Hello, world!');import * as React from 'react';
import { Code } from './Code';
export function BasicCode() {
return <Code fileName="hello.js">{`console.log('Hello, world!');`}</Code>;
}
Good user experience means showing meaningful feedback during loading.
The fallback content is also what search engines and AI bots see in the initial HTML, making it crucial for SEO and content discovery.
CodeHighlighter supports custom loading components and skeleton loaders:
import * as React from 'react';
import { Checkbox } from '@/components/Checkbox';
import styles from './CheckboxRed.module.css';
export function CheckboxRed() {
return <Checkbox className={styles.root} defaultChecked />;
}
import { CheckboxRed } from './CheckboxRed';
import { createDemo } from './createDemo';
export const DemoCheckboxRed = createDemo(import.meta.url, CheckboxRed);
When fallbackUsesExtraFiles is enabled, the fallback content receives all extra files from the selected variant. This means search engines and AI bots will see all the component's related files (CSS, utilities, etc.) in the initial HTML, improving content discoverability. Note that the complete list of filenames is always available for navigation, regardless of this setting:
import * as React from 'react';
import { Checkbox } from '@/components/Checkbox';
import styles from './CheckboxRed.module.css';
export function CheckboxRed() {
return <Checkbox className={styles.root} defaultChecked />;
}
.root {
background-color: #e5484d !important;
border-color: #e5484d !important;
}
import { CheckboxRed } from './CheckboxRed';
import { createDemo } from './createDemo';
export const DemoCheckboxRed = createDemo(import.meta.url, CheckboxRed);
When fallbackUsesAllVariants is enabled, the fallback content receives data for all available variants. This ensures that search engines and AI bots can see all implementation approaches (CSS Modules, Tailwind, etc.) in the initial HTML, making your documentation more comprehensive for automated indexing. Note that the complete list of filenames is always available for navigation, regardless of this setting:
import * as React from 'react';
import { Checkbox } from '@/components/Checkbox';
import styles from './CheckboxRed.module.css';
export function CheckboxRed() {
return <Checkbox defaultChecked className={styles.root} />;
}
.root {
background-color: #e5484d !important;
border-color: #e5484d !important;
}
import { CheckboxRed as CssModules } from './css-modules/CheckboxRed';
import { CheckboxRed as Tailwind } from './tailwind/CheckboxRed';
import { createDemoWithVariants } from './createDemo';
export const DemoCheckboxRed = createDemoWithVariants(import.meta.url, { CssModules, Tailwind });
import * as React from 'react';
import { Checkbox } from '@/components/Checkbox';
import styles from './CheckboxRed.module.css';
export function CheckboxRed() {
return <Checkbox defaultChecked className={styles.root} />;
}
import * as React from 'react';
import { Checkbox } from '@/components/Checkbox';
export function CheckboxRed() {
return <Checkbox defaultChecked className="bg-red-500" />;
}
import 'server-only';
import {
createDemoFactory,
createDemoWithVariantsFactory,
} from '@mui/internal-docs-infra/abstractCreateDemo';
import { DemoContentLoading } from './DemoContentLoading';
import { DemoContent } from './DemoContent';
/**
* Creates a demo component for displaying code examples with syntax highlighting.
* @param url Depends on `import.meta.url` to determine the source file location.
* @param component The component to be rendered in the demo.
* @param meta Additional meta for the demo.
*/
export const createDemo = createDemoFactory({
DemoContentLoading,
DemoContent,
fallbackUsesAllVariants: true,
});
/**
* Creates a demo component for displaying code examples with syntax highlighting.
* A variant is a different implementation style of the same component.
* @param url Depends on `import.meta.url` to determine the source file location.
* @param variants The variants of the component to be rendered in the demo.
* @param meta Additional meta for the demo.
*/
export const createDemoWithVariants = createDemoWithVariantsFactory({
DemoContentLoading,
DemoContent,
fallbackUsesAllVariants: true,
});
.root {
background-color: #e5484d !important;
border-color: #e5484d !important;
}
'use client';
import * as React from 'react';
import type { ContentLoadingProps } from '@mui/internal-docs-infra/CodeHighlighter/types';
import { Tabs } from '@/components/Tabs';
import { Select } from '@/components/Select';
import styles from './DemoContent.module.css';
import loadingStyles from './DemoContentLoading.module.css';
import '@wooorm/starry-night/style/light';
const variantNames: Record<string, string | undefined> = {
CssModules: 'CSS Modules',
};
export function DemoContentLoading(props: ContentLoadingProps<object>) {
const tabs = React.useMemo(
() =>
props.fileNames?.map((name) => ({
id: name || '',
name: name || '',
slug: name,
})),
[props.fileNames],
);
const variants = React.useMemo(
() =>
Object.keys(props.components || {}).map((variant) => ({
value: variant,
label: variantNames[variant] || variant,
})),
[props.components],
);
const onTabSelect = React.useCallback(() => {
// Handle tab selection
}, []);
return (
<div>
{Object.keys(props.extraSource || {}).map((slug) => (
<span key={slug} id={slug} className={styles.fileRefs} />
))}
<div className={styles.container}>
<div className={styles.demoSection}>{props.component}</div>
<div className={styles.codeSection}>
<div className={styles.header}>
<div className={styles.headerContainer}>
{tabs && (
<div className={styles.tabContainer}>
<Tabs
tabs={tabs}
selectedTabId={props.fileNames?.[0]}
onTabSelect={onTabSelect}
disabled={true}
/>
</div>
)}
<div className={styles.headerActions}>
{Object.keys(props.extraVariants || {}).length >= 1 && (
<Select items={variants} value={variants[0]?.value} disabled={true} />
)}
</div>
</div>
</div>
<div className={styles.code}>
<pre className={styles.codeBlock}>{props.source}</pre>
</div>
<div className={loadingStyles.extraFiles}>
{Object.keys(props.extraSource || {}).map((slug) => (
<pre key={slug}>{props.extraSource?.[slug]}</pre>
))}
</div>
<div className={loadingStyles.extraVariants}>
{Object.keys(props.extraVariants || {}).map((slug) => (
<div key={slug} className={loadingStyles.extraVariant}>
<span>{slug}</span>
<pre>
{Object.keys(props.extraVariants?.[slug].extraSource || {}).map((key) => (
<div key={key}>
<strong>{key}:</strong> {props.extraVariants?.[slug]?.extraSource?.[key]}
</div>
))}
</pre>
</div>
))}
</div>
</div>
</div>
</div>
);
}
'use client';
import * as React from 'react';
import type { ContentProps } from '@mui/internal-docs-infra/CodeHighlighter/types';
import { useDemo } from '@mui/internal-docs-infra/useDemo';
import { LabeledSwitch } from '@/components/LabeledSwitch';
import { Tabs } from '@/components/Tabs';
import { CopyButton } from '@/components/CopyButton';
import { Select } from '@/components/Select';
import styles from './DemoContent.module.css';
import '@wooorm/starry-night/style/light';
const variantNames: Record<string, string | undefined> = {
CssModules: 'CSS Modules',
};
export function DemoContent(props: ContentProps<object>) {
const demo = useDemo(props, { preClassName: styles.codeBlock });
const hasJsTransform = demo.availableTransforms.includes('js');
const isJsSelected = demo.selectedTransform === 'js';
const labels = { false: 'TS', true: 'JS' };
const toggleJs = React.useCallback(
(checked: boolean) => {
demo.selectTransform(checked ? 'js' : null);
},
[demo],
);
const tabs = React.useMemo(
() => demo.files.map(({ name, slug }) => ({ id: name, name, slug })),
[demo.files],
);
const variants = React.useMemo(
() =>
demo.variants.map((variant) => ({ value: variant, label: variantNames[variant] || variant })),
[demo.variants],
);
return (
<div>
{demo.allFilesSlugs.map(({ slug }) => (
<span key={slug} id={slug} className={styles.fileRefs} />
))}
<div className={styles.container}>
<div className={styles.demoSection}>{demo.component}</div>
<div className={styles.codeSection}>
<div className={styles.header}>
<div className={styles.headerContainer}>
<div className={styles.tabContainer}>
<Tabs
tabs={tabs}
selectedTabId={demo.selectedFileName}
onTabSelect={demo.selectFileName}
/>
</div>
<div className={styles.headerActions}>
<CopyButton copy={demo.copy} />
{demo.variants.length > 1 && (
<Select
items={variants}
value={demo.selectedVariant}
onValueChange={demo.selectVariant}
/>
)}
{hasJsTransform && (
<div className={styles.switchContainer}>
<LabeledSwitch
checked={isJsSelected}
onCheckedChange={toggleJs}
labels={labels}
/>
</div>
)}
</div>
</div>
</div>
<div className={styles.code}>{demo.selectedFile}</div>
</div>
</div>
</div>
);
}
.container {
border: 1px solid #d0cdd7;
border-radius: 8px;
}
.demoSection {
padding: 24px;
border-radius: 7px 7px 0 0;
}
.codeSection {
border-top: 1px solid #d0cdd7;
}
.header {
border-bottom: 1px solid #d0cdd7;
height: 48px;
position: relative;
}
.headerContainer {
position: absolute;
width: 100%;
display: flex;
justify-content: space-between;
gap: 8px;
}
.headerContainer::before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 1px;
margin: -1px;
background-color: #d0cdd7;
}
.headerActions {
display: flex;
align-items: center;
gap: 8px;
padding-right: 8px;
height: 48px;
}
.tabContainer {
display: flex;
align-items: center;
gap: 8px;
margin-left: -1px;
padding-bottom: 2px;
overflow-x: auto;
}
.switchContainer {
display: flex;
}
.switchContainerHidden {
display: none;
}
.code {
padding: 10px 6px;
}
.codeBlock {
margin: 0;
padding: 6px;
overflow-x: auto;
}
.extraFiles {
display: none;
}
.extraVariants {
display: none;
}
For more dynamic use cases, you can load code content from external sources or APIs at runtime:
Note
This example uses React Server Components (RSC) to load and parse files at request time instead of relying on the webpack loader for build-time precomputation. This approach is recommended when rendering based on dynamic routes (like
app/components/[component].tsx) where the content isn't known at build time. In these scenarios, you'd typically use the<CodeHighlighter>component directly rather than the factory pattern withcreateDemo().
import * as React from 'react';
import { Checkbox } from '@/components/Checkbox';
export function CheckboxBasic() {
return <Checkbox defaultChecked />;
}
import { createDemo } from './createDemo';
import { CheckboxBasic } from './CheckboxBasic';
export const DemoCheckboxBasic = createDemo(import.meta.url, CheckboxBasic, {
skipPrecompute: true,
});
CodeHighlighter can also be used on the client side, allowing you to load and parse code dynamically without server-side rendering. This is useful for applications that fetch code from APIs or databases.
You can do so by using the CodeProvider component, which provides the necessary context for CodeHighlighter to function correctly on the client.
CodeHighlighter also supports controlled scenarios where you need to manage code state externally. This is useful for interactive demos where code changes affect other parts of your application or when you need to synchronize code across multiple components.
You can use the CodeControllerContext for this purpose, which provides a controlled environment for managing code state in interactive scenarios.
Demos are exported as Demo${componentName}${demoName} e.g. DemoCheckboxBasic
This makes it easy to import a demo by simply typing <DemoCheckbox, and your IDE should display a list of existing demos to auto-import.
We recommend exporting a Title component attached within your createDemo() factory.
This allows us to use an automatically generated title and slug from the URL of the demo index.
This ensures that the link for the title is consistent with links to files within the demo itself.
e.g. ./app/components/checkbox/demos/advanced-keyboard/index.ts would generate a title of Advanced Keyboard and a slug of advanced-keyboard.
The URL for this demo would be /components/checkbox#advanced-keyboard and a file within it could be /components/checkbox#advanced-keyboard:file.ts.
Also, when MDX is rendered in GitHub, <DemoCheckboxAdvancedKeyboard.Title /> will be visible on the page because it contains the dot.
The import is also visible, so readers on GitHub will see this in the place of a demo:
import { DemoCheckboxAdvancedKeyboard } from './demos/advanced-keyboard';
<DemoCheckboxAdvancedKeyboard.Title />
Description of the demo.
If this convention isn't followed, URLs might deviate e.g. /components/checkbox#advanced-keyboard might not link to the correct demo.
// This is not recommended
import { DemoCheckboxAdvancedKeyboard } from './demos/advanced-keyboard'
### An Advanced Keyboard Demo
Description of the demo.
<DemoCheckboxAdvancedKeyboard />
This would result in the URL becoming /components/checkbox#an-advanced-keyboard-demo, and the file URL being /components/checkbox#advanced-keyboard:file.ts, which is not expected.
The description should be a short, concise explanation of what the demo does or why it shows a useful case. It is located directly in the component's docs because it can have rich contents like links, images, or other components. Try to avoid referring directly to the demo itself as when viewing the raw markdown, users won't see the code or renderings from the demo. Ideally, the reader can picture the demo in their mind without needing to see it.
Include a "See Demo" link after each demo component to allow users to navigate directly to the demo's source directory. This is especially useful when viewing the documentation in markdown editors or GitHub, where users can click the link to browse the demo files.
The link should point to the demo's directory using a relative path:
<DemoCheckboxBasic />
[See Demo](./demos/basic/)
This pattern makes it easy for developers to explore the demo's implementation, understand the file structure, and reference the source code when building their own components.
Use horizontal rules (---) to separate different sections of the demo. This allows the description of the previous demo to be visually distinct from the next demo.
import { DemoCheckboxBasic } from './demos/basic';
<DemoCheckboxBasic.Title />
Description of the basic demo.
<DemoCheckboxBasic />
[See Demo](./demos/basic/)
---
import { DemoCheckboxAdvanced } from './demos/second-demo';
<DemoCheckboxAdvanced.Title />
Description of the second demo.
<DemoCheckboxAdvanced />
[See Demo](./demos/second-demo/)
Within GitHub, it will display like this without rendering the demo and with See Demo underlined:
import { DemoCheckboxBasic } from './demos/basic';
<DemoCheckboxBasic.Title />
Description of the basic demo.
See Demo
---
import { DemoCheckboxAdvanced } from './demos/advanced';
<DemoCheckboxAdvanced.Title />
Description of the advanced demo.
See Demo
Tip
For Documentation Tooling: If you're building tooling to render these demos in a web interface, you can use a remark plugin to automatically remove the "See Demo" links and horizontal rule separators (
---) from the rendered output, since the interactive demos provide their own navigation. See thetransformMarkdownDemoLinksplugin documentation for implementation details.
Note
The
CodeControllerContextworks with all three rendering methods.
When rendering a code block, the user should consider the following options based on their use case:
Use the factory pattern optimal performance:
Advantages:
Requirements:
createDemo() in index.ts filesUse direct CodeHighlighter for dynamic content:
Advantages:
Trade-offs:
Use CodeHighlighter on the client with CodeProvider:
Advantages:
Trade-offs:
The CodeHighlighter component accepts the following props:
Content (React.ComponentType): Component to render the code contentname (string): Display name for the demoslug (string): URL-friendly identifiercode (Code): Precomputed code variantscomponents (Components): React components for live previewvariants (string[]): Available code variant namesvariant (string): Currently selected variantfileName (string): Display name for the fileurl (string): Unique identifier for the code sourcecontentProps (T): Additional props passed to the Content componentinitialVariant (string): Initial variant to display on first renderdefaultVariant (string): Default variant to fall back toprecompute (Code): Precomputed code for build-time optimizationcontrolled (boolean): Enable controlled modechildren (string): Direct code content (for CodeHighlighterBaseProps)highlightAfter ('init' | 'stream' | 'hydration' | 'idle'): When to highlight code
enhanceAfter ('init' | 'stream' | 'hydration' | 'idle'): When to enhance code
forceClient (boolean): Force client-side rendering onlyloadCodeMeta (LoadCodeMeta): Function to load code metadataloadVariantMeta (LoadVariantMeta): Function to load variant metadataloadSource (LoadSource): Function to load source codesourceParser (Promise of ParseSource): Parser for syntax highlightingsourceTransformers (SourceTransformer[]): Code transformersContentLoading (React.ComponentType): Loading state componentErrorHandler (React.ComponentType): Error display componentfallbackUsesExtraFiles (boolean): Include extra files content in fallbackfallbackUsesAllVariants (boolean): Include all variants content in fallbackSEO & AI Indexing Note: The
fallbackUsesExtraFilesandfallbackUsesAllVariantsoptions determine what content is included in the initial HTML that search engines and AI bots see. Enabling these options ensures better content discoverability and more comprehensive automated indexing of your code examples.
File Navigation: Regardless of these settings, the complete list of filenames (
fileNames) is always provided in the fallback content, ensuring users can see the file structure and navigate between files even during loading states.
The fallback content always receives certain data for navigation and UX consistency, while other content depends on the configuration:
Always Provided:
fileNames - Complete list of all available files for navigationname, slug, url - Basic demo metadatacomponent - The selected variant's React componentcomponents - Full components object (only when fallbackUsesAllVariants: true)Conditionally Provided:
source - Main file content (always for selected variant)extraSource - Extra files content (only when fallbackUsesExtraFiles: true)extraVariants - All variants data (only when fallbackUsesAllVariants: true)ContentLoading for quicker hydrationErrorHandler to display meaningful error messageshighlightAfter: 'idle' for non-critical code blocksCode not highlighting:
sourceParser is providedhighlightAfter timing is appropriateLoading states not showing:
ContentLoading componenthighlightAfter: 'stream' for loading statesfallbackUsesExtraFiles settingsPerformance issues:
highlightAfter: 'idle' for non-critical codecreateDemo() in index.ts filesPrecomputation not working:
createDemo() in an index.ts file./app/**/demos/*/index.tsVariants not switching:
components and variants| Prop | Type | Description |
|---|---|---|
| name | string | Display name for the code example, used for identification and titles |
| Content | ComponentType<ContentProps<{}>> | Component to render the code content and preview |
| ContentLoading | ComponentType<ContentLoadingProps<{}>> | Component to show while code is being loaded or processed |
| code | Code | Static code content with variants and metadata |
| components | Components | React components for live preview alongside code |
| contentProps | {} | Additional props passed to the Content component |
| controlled | boolean | Enable controlled mode for external code state management |
| defaultVariant | string | Fallback variant when the requested variant is not available |
| deferParsing | 'none' | 'json' | 'gzip' | Defer parsing and populating the AST into memory until the code is enhanced Applies only in production when RSC loading |
| enhanceAfter | 'init' | 'stream' | 'hydration' | 'idle' | When to enhance the code display with interactivity |
| fallbackUsesAllVariants | boolean | Whether fallback content should include all variants |
| fallbackUsesExtraFiles | boolean | Whether fallback content should include extra files |
| fileName | string | Currently selected file name |
| forceClient | boolean | Force client-side rendering even when server rendering is available |
| globalsCode | (string | Code)[] | Global static code snippets to inject, typically for styling or tooling |
| highlightAfter | 'init' | 'stream' | 'hydration' | 'idle' | When to perform syntax highlighting and code processing |
| initialVariant | string | Default variant to show on first load |
| language | string | Language for syntax highlighting (e.g., ‘tsx’, ‘css’). When provided, fileName is not required for parsing. |
| loadCodeMeta | LoadCodeMeta | Function to load code metadata from a URL |
| loadSource | LoadSource | Function to load raw source code and dependencies |
| loadVariantMeta | LoadVariantMeta | Function to load specific variant metadata |
| precompute | Code | Pre-computed code data from build-time optimization |
| slug | string | URL-friendly identifier for deep linking and navigation |
| sourceParser | Promise<ParseSource> | Promise resolving to a source parser for syntax highlighting |
| sourceTransformers | SourceTransformer[] | Array of source transformers for code processing (e.g., TypeScript to JavaScript) |
| url | string | Source URL where the code content originates from |
| variant | string | Currently selected variant name |
| variantType | string | What type of variants are available (e.g., a type |
| variants | string[] | Static variant names that should be fetched at runtime |
| children | string | Raw code string for simple use cases |
// Component mapping for live previews
type Components = {
[key: string]: React.ReactNode;
};
// Content rendering props (generic)
type ContentProps<T extends {}> = {
name?: string;
slug?: string;
code?: Code;
components?: Components;
} & T;
// Source can be string, HAST nodes, or serialized HAST
type VariantSource = string | HastNodes | { hastJson: string };
// Transform definitions with deltas
type Transforms = Record<
string,
{
delta: Delta;
fileName?: string;
}
>;
// Extra files for a variant
type VariantExtraFiles = {
[fileName: string]:
| string
| {
source?: VariantSource;
transforms?: Transforms;
skipTransforms?: boolean;
};
};
// Single variant configuration
type VariantCode = {
fileName?: string;
url?: string;
source?: VariantSource;
extraFiles?: VariantExtraFiles;
filesOrder?: string[];
transforms?: Transforms;
allFilesListed?: boolean;
skipTransforms?: boolean;
};
// Code structure for all variants
type Code = {
[key: string]: undefined | string | VariantCode;
};
// Controlled extra files (for editable demos)
type ControlledVariantExtraFiles = {
[fileName: string]: { source: string | null };
};
// Controlled variant code (for editable demos)
type ControlledVariantCode = {
fileName?: string;
url?: string;
source?: string | null;
extraFiles?: ControlledVariantExtraFiles;
filesOrder?: string[];
};
// Controlled code structure (for editable demos)
type ControlledCode = {
[key: string]: undefined | null | ControlledVariantCode;
};
// Options for controlling file loading behavior
interface LoadFileOptions {
/** Disable applying source transformers */
disableTransforms?: boolean;
/** Disable parsing source strings to AST */
disableParsing?: boolean;
/** Maximum recursion depth for loading nested extra files */
maxDepth?: number;
/** Set of already loaded file URLs to prevent circular dependencies */
loadedFiles?: Set<string>;
}
// Parse source code to HAST nodes
type ParseSource = (source: string, fileName: string) => HastNodes;
// Transform source code (e.g., TS to JS)
type TransformSource = (
source: string,
fileName: string,
) => Promise<
| Record<
string,
{
source: string;
fileName?: string;
}
>
| undefined
>;
// Source transformer configuration
type SourceTransformer = {
extensions: string[];
transformer: TransformSource;
};
type SourceTransformers = Array<SourceTransformer>;
// Load source code and extra files
type LoadSource = (url: string) => Promise<{
source: string;
extraFiles?: VariantExtraFiles;
extraDependencies?: string[];
}>;
// Load variant-specific metadata
type LoadVariantMeta = (variantName: string, url: string) => Promise<VariantCode>;
// Load code metadata from URL
type LoadCodeMeta = (url: string) => Promise<Code>;