Warning
This is an internal project, and is not intended for public use. No support or stability guarantees are provided.
The CodeProvider component provides client-side functions for fetching source code and highlighting it. It's designed for cases where you need to render code blocks or demos based on client-side state or dynamic content loading. It also provides heavy processing functions that are moved from individual components to the context for better performance and code splitting.
CodeProvider implements the Props Context Layering pattern by providing heavy functions via context that can't be serialized across the server-client boundary.
Use CodeProvider when you need:
Note
If you need interactive code editing with shared state management, use the
CodeControllerContextinstead.
The simplest way to use CodeProvider is to wrap components that need client-side highlighting:
console.log('Hello, world!');import * as React from 'react';
import { Code } from './CodeBlock';
export function BasicCode() {
return (
<Code fileName="example.js">{`console.log('Hello, world!');`}</Code>
);
}
For more dynamic use cases, you can provide custom loading functions that fetch code from external sources:
import * as React from 'react';
import { CodeProviderGitHub } from './CodeProviderGitHub';
import { DemoCheckboxBasic } from './demo-basic';
export function Docs() {
return (
<CodeProviderGitHub>
<DemoCheckboxBasic />
</CodeProviderGitHub>
);
}
The previous demo asks loadVariantMeta to enumerate every file a variant exposes up front. The recursive variant flips that around: loadCodeMeta only resolves the variant entry points, and loadSource parses each file with parseImportsAndComments and reports the relative imports as extraFiles. The framework then calls loadSource for each newly discovered file, so the import graph is walked one node at a time.
import * as React from 'react';
import { CodeProviderGitHub } from './CodeProviderGitHub';
import { DemoCheckboxBasic } from './demo-basic';
export function Docs() {
return (
<CodeProviderGitHub>
<DemoCheckboxBasic />
</CodeProviderGitHub>
);
}
| Concern | Eager (fetch-demo) | Recursive (fetch-demo-recursive) |
|---|---|---|
| Loader surface | loadCodeMeta, loadVariantMeta, loadSource | loadCodeMeta, loadSource |
| File discovery | One directory listing per variant via the Contents API | Imports are parsed inside loadSource; only files actually imported are fetched |
| Network shape | Many small Contents API calls up front, then raw fetches in parallel | A short serial chain (entry → its imports → their imports), but no Contents API per file |
| Source assumptions | Works even when sources don't have static imports (handy for assets, JSON, generated bundles) | Requires the host to ship parseable JS/TS/MDX/CSS so parseImportsAndComments can see imports |
| Best for | Demos that expose a known set of files (e.g. index.tsx + a sibling styles.css) | Demos with deeper, evolving import trees where listing every file up front is awkward |
| Stale risk | Each variant points at a tree URL; ref drift between variants is possible if you don't pin it | Pinning the entry to a commit SHA cascades to every recursively discovered file |
In practice the eager variant is the right starting point for documentation that mirrors a small, hand-curated set of files. Reach for the recursive variant once a demo grows beyond a couple of files or starts importing helpers from sibling directories — at that point the savings from on-demand resolution and SHA-pinned caching outweigh the extra request round-trips.
CodeProvider works seamlessly with CodeHighlighter for dynamic content:
import { CodeProvider } from '@mui/internal-docs-infra/CodeProvider';
import { CodeHighlighter } from '@mui/internal-docs-infra/CodeHighlighter';
import { CodeContent } from './CodeContent';
function DynamicCodeExample() {
return (
<CodeProvider>
<CodeHighlighter Content={CodeContent}>{`console.log("Dynamic code!");`}</CodeHighlighter>
</CodeProvider>
);
}
You can provide custom functions for loading code from different sources:
import { CodeProvider } from '@mui/internal-docs-infra/CodeProvider';
import type { LoadCodeMeta, LoadSource } from '@mui/internal-docs-infra/CodeHighlighter';
const loadCodeFromApi: LoadCodeMeta = async (url: string) => {
const response = await fetch(`/api/code/${encodeURIComponent(url)}`);
return response.json();
};
const loadSourceFromGitHub: LoadSource = async (url: string) => {
const response = await fetch(url.replace('file://', 'https://raw.githubusercontent.com/'));
const source = await response.text();
return { source };
};
function MyApp() {
return (
<CodeProvider loadCodeMeta={loadCodeFromApi} loadSource={loadSourceFromGitHub}>
{/* Your components */}
</CodeProvider>
);
}
Provides client-side functions for fetching source code and highlighting it. Designed for cases where you need to render code blocks or demos based on client-side state or dynamic content loading.
Implements the Props Context Layering pattern by providing heavy functions via context that can’t be serialized across the server-client boundary.
| Prop | Type | Description |
|---|---|---|
| loadCodeMeta | | Function to load code metadata from a URL |
| loadSource | | Function to load raw source code and dependencies |
| loadVariantMeta | | Function to load specific variant metadata |
| sourceEnhancers | | |
| children | | Child components that will have access to the code handling context |
When the runtime is a browser that supports Web Workers, CodeProvider automatically spawns a single dedicated worker per provider and uses it to syntax-highlight source code while the user is typing in an editable code block. This keeps the main thread responsive during fast keystrokes — the synchronous highlighter that runs after each commit can still take low single-digit milliseconds for large files, which is enough to drop a frame on slower devices.
import(...), so neither ships in SSR bundles and neither blocks first paint.postMessage. The worker calls createStarryNight(grammars) and acknowledges with init-ack. If grammar creation throws, the worker posts init-error and every queued parse request rejects.init-ack.Worker terminated.Each parse request accepts an optional AbortSignal. When a newer keystroke supersedes an older one, the consumer (typically useEditable's preParse hook) aborts the older signal so the worker's response is dropped on arrival rather than replacing fresher highlighted output. Pre-aborted signals reject synchronously without any cross-thread traffic.
The worker integration is fully automatic — no opt-in is required. A consumer that wraps an editable code block in CodeProvider will see worker-backed highlighting as long as Worker is defined and the editable's host (typically useCode) provides a setSource callback. When either condition is missing the editable falls back to the synchronous main-thread highlighter and the UX is unchanged.
Note
The same grammar chunk powers both the main-thread
parseSourceand the worker-thread parser, so enabling the worker does not double the grammar download cost.
CodeProvider high enough to cover all components that need highlightingCodeProvider is perfect for code that can't be precomputed| Feature | CodeProvider | Code Controller | CodeHighlighter |
|---|---|---|---|
| Purpose | Client-side highlighting & fetching | Interactive code editing | Displaying code with previews |
| State management | × No shared state | ✓ Shared editing state | × Component-level only |
| Dynamic loading | ✓ Custom loading functions | × Static content focused | ✓ Various loading options |
| User editing | × Display only | ✓ Real-time editing | × Display only |
| Build optimization | × Client-side only | ✓ Can use precomputation | ✓ Build-time optimization |
| Use case | Dynamic code display | Code playgrounds | Documentation sites |
Highlighting not working:
CodeProviderPerformance issues:
SSR errors:
CodeProvider automatically handles SSR safetyforceClient on CodeHighlighter when needed