MUI Docs Infra

Warning

This is an internal project, and is not intended for public use. No support or stability guarantees are provided.

Use Code Window

The useCodeWindow hook layers code-block-specific choreography on top of useScrollAnchor so a syntax-highlighted snippet can expand and collapse without the user losing their place — and without the horizontal scrollbar snapping in or out.

It is the recommended hook when you're building a collapsible code block. For non-code layouts, reach for useScrollAnchor directly.

By default this hook assumes a page-level layout: it compensates window scroll, and the offscreen check it uses to decide between anchors is measured against the window viewport. When your code block is rendered as a fixed-height window (its own overflow: auto region), attach the forwarded scrollContainerRef to that scrollable element and the hook compensates the panel's own scroll instead of the page — see Windowed code block. The offscreen check still measures against the window viewport.

What it does

On top of useScrollAnchor's page-scroll compensation, this hook:

  1. Picks the anchor for you. Looks for an element matching [data-frame-type="highlighted"], [data-frame-type="focus"] inside the container (the convention used by the highlighter pipeline). Falls back to the toggle ref when no such frame exists, or when the highlighted frame is offscreen on collapse (offscreen is measured against the window viewport). If neither anchor is available, anchorScroll no-ops.
  2. Animates the scrollbar gutter. Toggles a data-scrollbar-gutter attribute on the inner <pre> so your CSS can swap between a real horizontal scrollbar and equivalent padding-bottom instead of snapping when overflow appears or disappears. The CSS transition runs regardless of prefers-reduced-motion — if you want to honor it, gate the transition in your stylesheet.
  3. Slides scroll back to column 0 on collapse. Translates the inner <code> element back to scrollLeft = 0 via a compositor-driven WAAPI animation, so the focused region (which usually starts at the left edge) is back in view after collapsing. Honors prefers-reduced-motion.
  4. Cancels the most recent <pre> animation on unmount. Earlier <pre> animations (if containerRef was reattached to a different element during the component's lifetime) are left to garbage collection.

Basic Usage

Snippet

function quickSort(arr) {
  if (arr.length <= 1) {
    return arr;
  }
  const pivot = arr[0];
  const left = [];
  const right = [];
  for (let index = 1; index < arr.length; index += 1) {
    if (arr[index] < pivot) {
      left.push(arr[index]);
    } else {
      right.push(arr[index]);
    }
  }
  return [...quickSort(left), pivot, ...quickSort(right)];
}
'use client';
import * as React from 'react';
import { useCodeWindow } from '@mui/internal-docs-infra/useCodeWindow';
import styles from './CollapsibleSnippet.module.css';

const longSource = `function quickSort(arr) {
  if (arr.length <= 1) {
    return arr;
  }
  const pivot = arr[0];
  const left = [];
  const right = [];
  for (let index = 1; index < arr.length; index += 1) {
    if (arr[index] < pivot) {
      left.push(arr[index]);
    } else {
      right.push(arr[index]);
    }
  }
  return [...quickSort(left), pivot, ...quickSort(right)];
}`;

export function CollapsibleSnippet() {
  const [expanded, setExpanded] = React.useState(false);
  const { containerRef, toggleRef, anchorScroll } = useCodeWindow<HTMLButtonElement>();

  const onToggle = () => {
    // Anchor *before* the layout-changing state update.
    anchorScroll(expanded ? 'collapse' : 'expand');
    setExpanded((prev) => !prev);
  };

  return (
    <div ref={containerRef} className={styles.container}>
      <pre className={`${styles.pre} ${expanded ? styles.expanded : ''}`}>
        <code className={styles.code}>{longSource}</code>
      </pre>
      <button ref={toggleRef} type="button" onClick={onToggle} className={styles.toggle}>
        {expanded ? 'Collapse' : 'Expand'}
      </button>
    </div>
  );
}
import { useCodeWindow } from '@mui/internal-docs-infra/useCodeWindow';

function CollapsibleSnippet() {
  const [expanded, setExpanded] = React.useState(false);
  const { containerRef, toggleRef, anchorScroll } = useCodeWindow<HTMLButtonElement>();

  return (
    <div ref={containerRef}>
      <pre className={expanded ? 'expanded' : ''}>
        <code>{source}</code>
      </pre>
      <button
        ref={toggleRef}
        type="button"
        onClick={() => {
          // Anchor before the layout-changing state update.
          anchorScroll(expanded ? 'collapse' : 'expand');
          setExpanded((prev) => !prev);
        }}
      >
        {expanded ? 'Collapse' : 'Expand'}
      </button>
    </div>
  );
}

The hook expects a structure of <div ref={containerRef}><pre><code></code></pre></div>. The <pre> and <code> elements are how the gutter and scroll-back animations find their target — the hook locates them by querying the container's DOM (the first <pre> descendant wins), so no extra refs are needed. anchorScroll('expand' | 'collapse') uses the configured expandDuration / collapseDuration, so there's no need to pass a duration at the call site.

The first generic types the toggle element (HTMLButtonElement here), not the container — unlike useScrollAnchor, where the first generic types containerRef. The container is always typed as HTMLDivElement because the hook needs to query into it. An optional second generic types the scrollContainerRef element (see Windowed code block).

Windowed code block

By default the hook grows the document flow and compensates the page scroll. To cap the code block at a fixed height with its own internal scroll, wrap it in an overflow: auto element and attach scrollContainerRef to that wrapper. The anchor element whose layout changes still gets containerRef (the two must be different elements — a ResizeObserver on the fixed-height scroll container would never fire, since its border box stays capped). Compensation then nudges the panel's scrollTop instead of the page.

const { containerRef, scrollContainerRef, toggleRef, anchorScroll } = useCodeWindow<
  HTMLButtonElement,
  HTMLDivElement
>();

return (
  <div ref={scrollContainerRef} className={styles.codeWindow}>
    {/* fixed max-height + overflow: auto */}
    <div ref={containerRef}>
      <pre>
        <code>…</code>
      </pre>
    </div>
  </div>
);

Configuration

Defaults match the highlighter's CSS conventions, but every opinion is configurable:

useCodeWindow({
  expandDuration: 350, // ms — must match your CSS expand transition
  collapseDuration: 350, // ms — must match your CSS collapse transition
  scrollBackDuration: 300, // ms — set to 0 to disable
  anchorSelector: '[data-frame-type="highlighted"], [data-frame-type="focus"]',
  collapsibleProbeSelector: '[data-collapsible]',
});

expandDuration / collapseDuration

Default 350 ms each. Should equal the CSS transition duration on whichever property drives the height change (height with interpolate-size, max-height, etc.). The hook keeps the page-scroll compensation alive for this long, then hands control back to the user.

scrollBackDuration

Default 300 ms. The horizontal <code>-transform animation that returns the snippet to column 0 on collapse. The default matches the gutter animation; set to 0 to disable.

anchorSelector

Selector evaluated inside the container to pick the anchor. The first match wins. Defaults to [data-frame-type="highlighted"], [data-frame-type="focus"] — override only if your markup uses different data attributes.

collapsibleProbeSelector

Defaults to [data-collapsible]. When set, the expand transition only animates the gutter if at least one element matching this selector exists inside the <pre>. This avoids unnecessary attribute toggling for plain (non-truncated) snippets where no scrollbar will appear after expansion. Pass a falsy value (undefined or false) to skip the gutter animation on expand entirely.

CSS contract

The hook flips a data-scrollbar-gutter attribute on the inner <pre>. To get the gutter animation, your stylesheet must provide the corresponding rules. The example below references a --scrollbar-h custom property that you must define yourself — either via scrollbar-gutter: stable math, a JS-set value on :root, or a measured value from your design system. The hook does not expose its internal scrollbar measurement.

/* Just before collapse: lock overflow and reserve the scrollbar's space */
pre[data-scrollbar-gutter='collapse-from'] {
  overflow-x: hidden;
  padding-bottom: var(--scrollbar-h);
}
/* Animate to the resting padding */
pre[data-scrollbar-gutter='collapse-to'] {
  overflow-x: hidden;
  padding-bottom: 12px;
  transition: padding-bottom 0.35s ease;
}
/* Mirror for expand if you want the scrollbar to fade in */
pre[data-scrollbar-gutter='expand-from'] {
  overflow-x: hidden;
  padding-bottom: 12px;
}
pre[data-scrollbar-gutter='expand-to'] {
  overflow-x: hidden;
  padding-bottom: var(--scrollbar-h);
  transition: padding-bottom 0.35s ease;
}

Without these rules the attribute is harmless — it just toggles silently. If you change expandDuration / collapseDuration, update the 0.35s values above to match.

Animating the frames

The hook choreographs the scroll and the gutter — growing and shrinking the frames is your CSS. Each frame outside the focused region collapses to height: 0 and animates back to height: auto (via interpolate-size) when the toggle expands the block:

@supports (interpolate-size: allow-keywords) {
  /* Collapsed: frames outside the focused region have no height */
  .frame:not([data-frame-type]) {
    interpolate-size: allow-keywords;
    height: 0;
    overflow: clip;
    transition: height 0.3s ease;
  }

  /* Expanded */
  :checked ~ .code .frame:not([data-frame-type]) {
    height: auto;
  }
}

Staggering the expand

A long block opens best when the frames nearest the focused region move first and shove the rest off-screen, rather than every frame growing in lock-step. Drive the whole effect from one custom property: frames directly bordering the focused region grow at full speed, while frames farther out wait out the first third — the bordering frames push them off-screen — then cover their height in the remaining two thirds, so every frame still lands on time.

pre {
  --frame-expand-duration: 0.3s;
  --frame-expand-stagger-delay: calc(var(--frame-expand-duration) / 3);
  --frame-expand-stagger-duration: calc(
    var(--frame-expand-duration) - var(--frame-expand-stagger-delay)
  );
}

/* Far frames are delayed by a third, then run for the rest of the duration */
:checked ~ .code .frame:not([data-frame-type]) {
  transition: height var(--frame-expand-stagger-duration) ease var(--frame-expand-stagger-delay);
}

/* Frames directly bordering the focused region run at full speed with no delay.
   `:has(+ focus)` is the frame above the region; `focus + .frame` is the one below. */
:checked ~ .code .frame:not([data-frame-type]):has(+ .frame[data-frame-type='focus']),
:checked ~ .code .frame[data-frame-type='focus'] + .frame:not([data-frame-type]) {
  transition: height var(--frame-expand-duration) ease;
}

The example above borders a single focus region; the demos generalize the "visible" side to any focused frame type (padding-top, padding-bottom, highlighted) and treat the hidden -unfocused overflow frames as bordering too — see the CollapsibleContent.module.css source for the full selector.

Examples

These demos all use enhanceCodeEmphasis directives (@highlight, @focus, @padding) to mark which lines belong to the focus area. useCodeWindow then handles the choreography — picking the anchor, animating the gutter, and keeping your scroll position pinned.

Collapsible Code Block

A code block that starts collapsed, revealing only the highlighted and padding frames. Click Expand to show the full source.

Collapsible Code Block

import * as React from 'react';
import { fetchUser } from './api';

interface User {
  name: string;
  email: string;
}

export function UserProfile({ id }: { id: string }) {
  const [user, setUser] = React.useState<User | null>(null);

  React.useEffect(() => {
    let cancelled = false;
    fetchUser(id).then((data) => {
      if (!cancelled) {
        setUser(data);
      }
    });
    return () => {
      cancelled = true;
    };
  }, [id]);

  if (!user) {
    return <p>Loading…</p>;
  }

  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}
import * as React from 'react';
import type { Code as CodeType } from '@mui/internal-docs-infra/CodeHighlighter/types';
import { parseImportsAndComments } from '@mui/internal-docs-infra/pipeline/loaderUtils';
import { EMPHASIS_COMMENT_PREFIX } from '@mui/internal-docs-infra/pipeline/enhanceCodeEmphasis';
import { Code } from './Code';

const source = `import * as React from 'react';
import { fetchUser } from './api';

interface User {
  name: string;
  email: string;
}

export function UserProfile({ id }: { id: string }) {
  const [user, setUser] = React.useState<User | null>(null);

  // @highlight-start
  React.useEffect(() => {
    let cancelled = false;
    fetchUser(id).then((data) => {
      if (!cancelled) {
        setUser(data);
      }
    });
    return () => {
      cancelled = true;
    };
  }, [id]);
  // @highlight-end

  if (!user) {
    return <p>Loading…</p>;
  }

  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}`;

export async function CollapsibleCode() {
  const { code: strippedSource, comments } = await parseImportsAndComments(source, '/demo.tsx', {
    removeCommentsWithPrefix: [EMPHASIS_COMMENT_PREFIX],
    notableCommentsPrefix: [EMPHASIS_COMMENT_PREFIX],
  });

  const code: CodeType = {
    Default: {
      fileName: 'UserProfile.tsx',
      language: 'tsx',
      source: strippedSource!,
      comments,
    },
  };

  return <Code code={code} />;
}

Collapse to Empty

The same block, but rendered with the collapseToEmpty flag (<Code code={code} collapseToEmpty />). Instead of collapsing to a focus window, the block starts completely hidden — only the Expand toggle shows until the reader opens it. collapseToEmpty is a render-time flag: it demotes the collapsed-visible frames so the existing collapse CSS hides everything, without changing the precomputed source.

Collapse to Empty

import { defineConfig } from 'some-bundler';

// A long config most readers will scroll past — collapse it to nothing by
// default and let them expand it on demand.
export default defineConfig({
  entry: './src/index.ts',
  format: ['esm', 'cjs'],
  target: 'es2022',
  sourcemap: true,
  dts: true,
  clean: true,
  external: ['react', 'react-dom'],
  esbuildOptions(options) {
    options.banner = {
      js: '"use client";',
    };
  },
});
import * as React from 'react';
import type { Code as CodeType } from '@mui/internal-docs-infra/CodeHighlighter/types';
import { Code } from './Code';

const source = `import { defineConfig } from 'some-bundler';

// A long config most readers will scroll past — collapse it to nothing by
// default and let them expand it on demand.
export default defineConfig({
  entry: './src/index.ts',
  format: ['esm', 'cjs'],
  target: 'es2022',
  sourcemap: true,
  dts: true,
  clean: true,
  external: ['react', 'react-dom'],
  esbuildOptions(options) {
    options.banner = {
      js: '"use client";',
    };
  },
});`;

export function CollapseToEmptyCode() {
  const code: CodeType = {
    Default: {
      fileName: 'bundler.config.ts',
      language: 'ts',
      source,
    },
  };

  // `collapseToEmpty` collapses the whole block to an empty window: nothing is
  // shown until the reader clicks Expand. It is a render-time flag — the
  // precomputed source is unchanged.
  return <Code code={code} collapseToEmpty />;
}

Collapsible Code Block with Transform

A collapsible code block that also exposes a TypeScript → JavaScript transform toggle. Use this to verify that swapping transforms and toggling collapse work together — the actions menu surfaces the JS toggle while the collapse choreography keeps the focused region pinned.

Collapsible Code Block with Transform

import * as React from 'react';

interface User {
  id: string;
  name: string;
  email: string;
}

interface UserListProps {
  users: User[];
  onSelect: (user: User) => void;
}

export function UserList({ users, onSelect }: UserListProps) {
  const [query, setQuery] = React.useState<string>('');

  const filtered = users.filter((user: User) =>
    user.name.toLowerCase().includes(query.toLowerCase()),
  );

  return (
    <ul>
      {filtered.map((user: User) => (
        <li key={user.id} onClick={() => onSelect(user)}>
          {user.name}
        </li>
      ))}
    </ul>
  );
}
import * as React from 'react';
import type { Code as CodeType } from '@mui/internal-docs-infra/CodeHighlighter/types';
import { parseImportsAndComments } from '@mui/internal-docs-infra/pipeline/loaderUtils';
import { FOCUS_COMMENT_PREFIX } from '@mui/internal-docs-infra/pipeline/enhanceCodeEmphasis';
import { TypescriptToJavascriptTransformer } from '@mui/internal-docs-infra/pipeline/transformTypescriptToJavascript';
import { Code } from './Code';

const source = `import * as React from 'react';

interface User {
  id: string;
  name: string;
  email: string;
}

interface UserListProps {
  users: User[];
  onSelect: (user: User) => void;
}

// @focus-start
export function UserList({ users, onSelect }: UserListProps) {
  const [query, setQuery] = React.useState<string>('');

  const filtered = users.filter((user: User) =>
    user.name.toLowerCase().includes(query.toLowerCase()),
  );

  return (
    <ul>
      {filtered.map((user: User) => (
        <li key={user.id} onClick={() => onSelect(user)}>
          {user.name}
        </li>
      ))}
    </ul>
  );
}
// @focus-end`;

const sourceTransformers = [TypescriptToJavascriptTransformer];

export async function CollapsibleTransform() {
  const { code: strippedSource, comments } = await parseImportsAndComments(
    source,
    '/UserList.tsx',
    {
      removeCommentsWithPrefix: [FOCUS_COMMENT_PREFIX],
      notableCommentsPrefix: [FOCUS_COMMENT_PREFIX],
    },
  );

  const code: CodeType = {
    Default: {
      fileName: 'UserList.tsx',
      language: 'tsx',
      source: strippedSource!,
      comments,
    },
  };

  return <Code code={code} sourceTransformers={sourceTransformers} />;
}

Collapsible Editor

A collapsible code block that is also editable. Combining collapse with live editing is what surfaces the trickiest caret behaviors: navigating the cursor past the visible top or bottom expands the window, the clipped indent gutter is respected, and edits re-highlight in place. Use this to verify cursor movement, frame-edge navigation, and flash-free structural edits inside a collapsed window.

Collapsible Editor

import * as React from 'react';
import { fetchUser } from './api';

interface User {
  name: string;
  email: string;
}

export function UserProfile({ id }: { id: string }) {
  const [user, setUser] = React.useState<User | null>(null);

  React.useEffect(() => {
    let cancelled = false;
    fetchUser(id).then((data) => {
      if (!cancelled) {
        setUser(data);
      }
    });
    return () => {
      cancelled = true;
    };
  }, [id]);

  if (!user) {
    return <p>Loading…</p>;
  }

  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}
import 'server-only';

import * as React from 'react';
import { CodeHighlighter } from '@mui/internal-docs-infra/CodeHighlighter';
import type { Code as CodeType } from '@mui/internal-docs-infra/CodeHighlighter/types';
import { createParseSource } from '@mui/internal-docs-infra/pipeline/parseSource';
import {
  createEnhanceCodeEmphasis,
  EMPHASIS_COMMENT_PREFIX,
} from '@mui/internal-docs-infra/pipeline/enhanceCodeEmphasis';
import { parseImportsAndComments } from '@mui/internal-docs-infra/pipeline/loaderUtils';

import { CollapsibleContentLazy } from './CollapsibleContentLazy';
import { CollapsibleContentLoading } from './CollapsibleContentLoading';
import { CodeController } from './CodeController';

const sourceParser = createParseSource();
// Must match the global `DemoCodeProvider` emphasis config (see
// `docs/demo-data/code-provider/layout.tsx`). This block is EDITABLE: the
// controlled view re-parses the source from its editable string (dropping the
// precomputed frames and the `appliedEnhancers` marker) and re-enhances it with
// the PROVIDER's enhancer. The server precompute here builds the loading
// fallback, so it must window identically or the fallback won't match the live
// editor (e.g. a missing `padding-top` frame).
const sourceEnhancers = [
  createEnhanceCodeEmphasis({ paddingFrameMaxSize: 2, focusFramesMaxSize: 18 }),
];

// A source long enough (and indented) to collapse into a windowed view with a
// clipped indent gutter. The `@highlight` block becomes the focused/visible
// region while collapsed; the surrounding lines are clipped.
const source = `import * as React from 'react';
import { fetchUser } from './api';

interface User {
  name: string;
  email: string;
}

export function UserProfile({ id }: { id: string }) {
  const [user, setUser] = React.useState<User | null>(null);

  // @highlight-start
  React.useEffect(() => {
    let cancelled = false;
    fetchUser(id).then((data) => {
      if (!cancelled) {
        setUser(data);
      }
    });
    return () => {
      cancelled = true;
    };
  }, [id]);
  // @highlight-end

  if (!user) {
    return <p>Loading…</p>;
  }

  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}`;

/**
 * A collapsible code block that is ALSO editable — the combination needed to
 * reproduce the collapsed-window editing bugs (last-indent erase jump, ArrowUp
 * scroll anchor). It reuses the collapsible `CollapsibleContent` (which owns the
 * `useCodeWindow` collapse + the expand checkbox) and turns on editing by
 * wrapping it in a `CodeController` and passing `controlled` to the highlighter.
 */
export async function CollapsibleEditor() {
  const { code: strippedSource, comments } = await parseImportsAndComments(source, '/demo.tsx', {
    removeCommentsWithPrefix: [EMPHASIS_COMMENT_PREFIX],
    notableCommentsPrefix: [EMPHASIS_COMMENT_PREFIX],
  });

  const code: CodeType = {
    Default: {
      fileName: 'UserProfile.tsx',
      language: 'tsx',
      source: strippedSource!,
      comments,
    },
  };

  return (
    <CodeController>
      <CodeHighlighter
        code={code}
        controlled
        Content={CollapsibleContentLazy}
        ContentLoading={CollapsibleContentLoading}
        sourceParser={sourceParser}
        sourceEnhancers={sourceEnhancers}
      />
    </CodeController>
  );
}

Collapsible Demo

A full demo component with a live preview, file tabs, and collapsible code. The code section starts collapsed, showing only the focused region.

Collapsible Demo

Counter.tsx
'use client';

import * as React from 'react';

export function Counter() {
  const [count, setCount] = React.useState(0);

  return (
    <button type="button" onClick={() => setCount((c) => c + 1)}>
      Count: {count}
    </button>
  );
}

@focus with Multiple Regions

Uses @highlight on the formatDate call and standalone @focus-start/@focus-end around the useEffect block. The useEffect region is focused (padding frames surround it) but has no line-level highlighting, while the formatDate line has line-level highlighting but is unfocused.

@focus Directive

import * as React from 'react';
import { formatDate } from './formatDate';
import { fetchEvents } from './fetchEvents';

const today = formatDate(new Date());

export function Calendar() {
  const [events, setEvents] = React.useState([]);

  React.useEffect(() => {
    fetchEvents(today).then(setEvents);
  }, []);

  return (
    <div className="calendar">
      <h2>{today}</h2>
      <ul>
        {events.map((event) => (
          <li key={event.id}>{event.title}</li>
        ))}
      </ul>
    </div>
  );
}
import * as React from 'react';
import type { Code as CodeType } from '@mui/internal-docs-infra/CodeHighlighter/types';
import { parseImportsAndComments } from '@mui/internal-docs-infra/pipeline/loaderUtils';
import {
  EMPHASIS_COMMENT_PREFIX,
  FOCUS_COMMENT_PREFIX,
} from '@mui/internal-docs-infra/pipeline/enhanceCodeEmphasis';
import { Code } from './Code';

const source = `import * as React from 'react';
import { formatDate } from './formatDate';
import { fetchEvents } from './fetchEvents';

const today = formatDate(new Date()); // @highlight

export function Calendar() {
  const [events, setEvents] = React.useState([]);

  // @focus-start
  React.useEffect(() => {
    fetchEvents(today).then(setEvents);
  }, []);
  // @focus-end

  return (
    <div className="calendar">
      <h2>{today}</h2>
      <ul>
        {events.map((event) => (
          <li key={event.id}>{event.title}</li>
        ))}
      </ul>
    </div>
  );
}`;

export async function FocusCode() {
  const { code: strippedSource, comments } = await parseImportsAndComments(source, '/demo.tsx', {
    removeCommentsWithPrefix: [EMPHASIS_COMMENT_PREFIX, FOCUS_COMMENT_PREFIX],
    notableCommentsPrefix: [EMPHASIS_COMMENT_PREFIX, FOCUS_COMMENT_PREFIX],
  });

  const code: CodeType = {
    Default: {
      fileName: 'Calendar.tsx',
      language: 'tsx',
      source: strippedSource!,
      comments,
    },
  };

  return <Code code={code} />;
}

Indent Shifting

With no padding configured, only highlighted/focus and normal frames are produced. Opt in to the data-frame-indent attribute on region frames by passing emitFrameIndent: true to createEnhanceCodeEmphasis — CSS can then read it to know how far the code is indented. This demo highlights an import statement (indent 0) and uses standalone @focus-start/@focus-end around the deeply nested <DatePicker> usage (indent 3). When collapsed, the focused frame shifts left by 6ch (3 indent levels × 2ch per level) so it aligns with the left edge, then resets when expanded.

Indent Shifting

import * as React from 'react';
import { DatePicker } from './DatePicker';

export function ScheduleView() {
  const [date, setDate] = React.useState(null);

  return (
    <main>
      <header>
        <h1>Schedule</h1>
      </header>
      <section>
        <form>
          <label htmlFor="date">Pick a date</label>
          <DatePicker
            id="date"
            value={date}
            onChange={setDate}
            minDate={new Date()}
            format="MM/dd/yyyy"
          />
        </form>
      </section>
      <footer>
        <p>All times shown in UTC</p>
      </footer>
    </main>
  );
}
import * as React from 'react';
import type { Code as CodeType } from '@mui/internal-docs-infra/CodeHighlighter/types';
import { parseImportsAndComments } from '@mui/internal-docs-infra/pipeline/loaderUtils';
import {
  EMPHASIS_COMMENT_PREFIX,
  FOCUS_COMMENT_PREFIX,
} from '@mui/internal-docs-infra/pipeline/enhanceCodeEmphasis';
import { CodeIndent } from './CodeIndent';

const source = `import * as React from 'react';
import { DatePicker } from './DatePicker'; // @highlight

export function ScheduleView() {
  const [date, setDate] = React.useState(null);

  return (
    <main>
      <header>
        <h1>Schedule</h1>
      </header>
      <section>
        <form>
          <label htmlFor="date">Pick a date</label>
          {/* @focus-start */}
          <DatePicker
            id="date"
            value={date}
            onChange={setDate}
            minDate={new Date()}
            format="MM/dd/yyyy"
          />
          {/* @focus-end */}
        </form>
      </section>
      <footer>
        <p>All times shown in UTC</p>
      </footer>
    </main>
  );
}`;

export async function IndentCode() {
  const { code: strippedSource, comments } = await parseImportsAndComments(source, '/demo.tsx', {
    removeCommentsWithPrefix: [EMPHASIS_COMMENT_PREFIX, FOCUS_COMMENT_PREFIX],
    notableCommentsPrefix: [EMPHASIS_COMMENT_PREFIX, FOCUS_COMMENT_PREFIX],
  });

  const code: CodeType = {
    Default: {
      fileName: 'ScheduleView.tsx',
      language: 'tsx',
      source: strippedSource!,
      comments,
    },
  };

  return <CodeIndent code={code} />;
}

Focus Max Size

When focusFramesMaxSize is set and a highlighted region exceeds that limit, a focused window is taken from the start of the region, and the remaining overflow lines are marked as unfocused. This demo uses focusFramesMaxSize: 6 with the <form> JSX highlighted (10 lines). When collapsed, only the first 6 lines of the region are visible; expanding reveals the full highlighted region with the overflow lines.

Focus Max Size

import * as React from 'react';

interface FormData {
  name: string;
  email: string;
  message: string;
}

export function ContactForm() {
  const [form, setForm] = React.useState<FormData>({
    name: '',
    email: '',
    message: '',
  });

  const handleSubmit = async (event: React.FormEvent) => {
    event.preventDefault();
    const response = await fetch('/api/contact', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(form),
    });
    if (!response.ok) {
      throw new Error('Failed to submit');
    }
    setForm({ name: '', email: '', message: '' });
  };

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="name">Name</label>
      <input id="name" value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} />
      <label htmlFor="email">Email</label>
      <input id="email" value={form.email} onChange={(e) => setForm({ ...form, email: e.target.value })} />
      <label htmlFor="message">Message</label>
      <textarea id="message" value={form.message} onChange={(e) => setForm({ ...form, message: e.target.value })} />
      <button type="submit">Send</button>
    </form>
  );
}
import * as React from 'react';
import type { Code as CodeType } from '@mui/internal-docs-infra/CodeHighlighter/types';
import { parseImportsAndComments } from '@mui/internal-docs-infra/pipeline/loaderUtils';
import { EMPHASIS_COMMENT_PREFIX } from '@mui/internal-docs-infra/pipeline/enhanceCodeEmphasis';
import { CodeMaxSize } from './CodeMaxSize';

const source = `import * as React from 'react';

interface FormData {
  name: string;
  email: string;
  message: string;
}

export function ContactForm() {
  const [form, setForm] = React.useState<FormData>({
    name: '',
    email: '',
    message: '',
  });

  const handleSubmit = async (event: React.FormEvent) => {
    event.preventDefault();
    const response = await fetch('/api/contact', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(form),
    });
    if (!response.ok) {
      throw new Error('Failed to submit');
    }
    setForm({ name: '', email: '', message: '' });
  };

  return (
    // @highlight-start
    <form onSubmit={handleSubmit}>
      <label htmlFor="name">Name</label>
      <input id="name" value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} />
      <label htmlFor="email">Email</label>
      <input id="email" value={form.email} onChange={(e) => setForm({ ...form, email: e.target.value })} />
      <label htmlFor="message">Message</label>
      <textarea id="message" value={form.message} onChange={(e) => setForm({ ...form, message: e.target.value })} />
      <button type="submit">Send</button>
    </form>
    // @highlight-end
  );
}`;

export async function MaxSizeCode() {
  const { code: strippedSource, comments } = await parseImportsAndComments(source, '/demo.tsx', {
    removeCommentsWithPrefix: [EMPHASIS_COMMENT_PREFIX],
    notableCommentsPrefix: [EMPHASIS_COMMENT_PREFIX],
  });

  const code: CodeType = {
    Default: {
      fileName: 'ContactForm.tsx',
      language: 'tsx',
      source: strippedSource!,
      comments,
    },
  };

  return <CodeMaxSize code={code} />;
}

API Reference

Layered helper that combines useScrollAnchor with the additional choreography needed when expanding/collapsing a syntax-highlighted code block.

On top of the page-scroll compensation provided by useScrollAnchor, it:

  • Selects an anchor inside the container (highlighted or focus frame), falling back to the toggle when the primary anchor is offscreen on collapse.
  • Drives a data-scrollbar-gutter attribute on the inner <pre> so the consumer’s CSS can swap between a real horizontal scrollbar and equivalent padding-bottom without a snap.
  • Smoothly returns the <code> element’s scrollLeft to 0 on collapse via a compositor-driven transform, so the focused region (which usually starts at column 0) is back in view after collapse.

The hook expects a structure like:

<div ref={containerRef}>
  <pre>
    <code>...</code>
  </pre>
  <button ref={toggleRef}>Expand</button>
</div>

Anchor selection and the collapsible probe are configurable so it works with any highlighter that marks frames with data attributes.

PropertyTypeDescription
expandDuration
number | undefined

Duration of the expand transition in ms. Should match the CSS transition on the collapsible container. Used to size the page-scroll compensation window.

collapseDuration
number | undefined

Duration of the collapse transition in ms. Should match the CSS transition on the collapsible container.

scrollBackDuration
number | undefined

Duration of the smooth scroll-back animation that returns the <code> element’s scrollLeft to 0 on collapse. Set to 0 to disable. Honors prefers-reduced-motion.

anchorSelector
string | undefined

CSS selector(s) used to find the anchor element inside the container. The first match wins. Falls back to the toggle ref when no match exists, or when the match is offscreen on collapse.

collapsibleProbeSelector
string | undefined

CSS selector that, when present inside the <pre>, opts the expand transition into the scrollbar-gutter animation. Useful when expansion can reveal previously hidden long lines and the horizontal scrollbar would otherwise appear with a snap.

Return Type
UseCodeWindowResult
KeyTypeRequired
containerRef
React.RefObject<HTMLDivElement | null>
Yes
scrollContainerRef
React.RefObject<HTMLElement | null>
Yes
toggleRef
React.RefObject<HTMLElement | null>
Yes
anchorScroll
(direction: 'collapse' | 'expand') => void
Yes
UseCodeWindowOptions
type UseCodeWindowOptions = {
  /**
   * Duration of the expand transition in ms. Should match the CSS
   * transition on the collapsible container. Used to size the
   * page-scroll compensation window.
   * @default 350
   */
  expandDuration?: number;
  /**
   * Duration of the collapse transition in ms. Should match the CSS
   * transition on the collapsible container.
   * @default 350
   */
  collapseDuration?: number;
  /**
   * Duration of the smooth scroll-back animation that returns the
   * `<code>` element's `scrollLeft` to `0` on collapse. Set to `0`
   * to disable. Honors `prefers-reduced-motion`.
   * @default 300
   */
  scrollBackDuration?: number;
  /**
   * CSS selector(s) used to find the anchor element inside the
   * container. The first match wins. Falls back to the toggle ref
   * when no match exists, or when the match is offscreen on collapse.
   * @default '[data-frame-type="highlighted"], [data-frame-type="focus"]'
   */
  anchorSelector?: string;
  /**
   * CSS selector that, when present inside the `<pre>`, opts the
   * expand transition into the scrollbar-gutter animation. Useful
   * when expansion can reveal previously hidden long lines and the
   * horizontal scrollbar would otherwise appear with a snap.
   * @default '[data-collapsible]'
   */
  collapsibleProbeSelector?: string;
}
UseCodeWindowResult
type UseCodeWindowResult<
  ToggleElement extends HTMLElement = HTMLElement,
  ScrollElement extends HTMLElement = HTMLElement,
> = {
  /** Ref to attach to the collapsible container element. */
  containerRef: React.RefObject<HTMLDivElement | null>;
  /**
   * Optional ref to attach to a scrollable ancestor that should be
   * compensated instead of the page. Attach it when the code block is
   * rendered as a fixed-height "window" (its own `overflow: auto` region)
   * so the anchor stays put against the panel's own scroll rather than the
   * page. When left unattached, the page is compensated — the right default
   * for code that grows the document flow. Forwarded from `useScrollAnchor`.
   *
   * When attached, this element is also treated as the horizontal scroll
   * owner: the scrollbar-gutter swap (`data-scrollbar-gutter`) and the
   * collapse scroll-back run on it instead of the inner `<pre>`. Use this when
   * the window owns both scroll axes so the horizontal scrollbar sits at the
   * window's edge (in view) rather than at the bottom of the inner `<pre>`,
   * which can extend past the window's height and scroll out of view. Your
   * gutter CSS must then key off this element's attribute.
   */
  scrollContainerRef: React.RefObject<ScrollElement | null>;
  /**
   * Ref to attach to the toggle element. Used as a fallback anchor
   * when the primary anchor is offscreen on collapse.
   */
  toggleRef: React.RefObject<ToggleElement | null>;
  /**
   * Call **just before** flipping the expanded/collapsed state. The
   * page will scroll so the anchor element stays put while the
   * container animates.
   */
  anchorScroll: (direction: 'collapse' | 'expand') => void;
}
  • useScrollAnchor — the underlying page-scroll compensation primitive. Use directly for non-code layouts.
  • enhanceCodeEmphasis — adds the [data-frame-type] markers this hook anchors to.