Note

Wide MDX CodeBlocks

This builds on Create a CodeBlock Component for MDX. This note adds an opt-in wide flag so a few long samples can breathe without making every block full width. I noticed Josh Comeau does this on his Anchor Positioning post: the paragraphs stay narrow, and one demo is allowed to run wider, toward the table of contents.

Why wide code blocks?

On this site, the majority of the content—like this note—render inside a prose column with a max width. That keeps paragraphs readable, but long code lines wrap or scroll inside a narrow box. Mostly this is fine, but I wanted to visualize specific scripts in their entirety.

I wanted to add one extra word on the fence, wide. In markdown, the text after the language on a fence is meta:

```bash 
# this block is normal
```
```bash wide
# this block is wide. It may overflow the prose column
```

Fence meta

When MDX compiles your file, it runs a pipeline before getMDXComponents() sees anything. Fence meta is parsed, then dropped when that tree becomes HTML-ish nodes. By the time your pre override runs, wide is already gone unless something copies it forward as a prop.

Listed in the MDX Extending docs is a plugin rehype-mdx-code-props for this: it interprets the code meta as JSX props.

Wiring it up

Install

npm install rehype-mdx-code-props

Wire it into shared compile options next to getMDXComponents():

import rehypeMdxCodeProps from "rehype-mdx-code-props";

export const mdxRemoteOptions = {
  parseFrontmatter: false,
  mdxOptions: {
    rehypePlugins: [rehypeMdxCodeProps],
  },
};

Pass mdxRemoteOptions into compileMDX on every MDX page.

The same plugin can carry other meta later (filename="...", showLineNumbers, and so on).

Read the flag in mdxComponents

Update the pre override to take wide and pass it to CodeBlock:

type PreOverrideProps = {
  children?: ReactNode;
  wide?: boolean;
};

pre: ({ children, wide }: PreOverrideProps) => {
  const child = children as ReactElement<CodeChildProps>;
  const className = child?.props?.className ?? "";
  const lang = className.replace("language-", "") || "plaintext";
  const code = child?.props?.children ?? "";

  return <CodeBlock code={String(code)} lang={lang} wide={Boolean(wide)} />;
},

Add wide to CodeBlockProps and append a global class when it is true:

export function CodeBlock({ code, lang, wide = false }: CodeBlockProps) {
  // ...
  return (
    <div className={`${styles.wrapper}${wide ? " code-wide" : ""}`}>
      {/* ... */}
    </div>
  );
}

Use a global class name (code-wide) rather than a CSS module hash so layout styles in utils.css can target it from .prose.

That is the full MDX path: wide on the fence, through rehype-mdx-code-props, onto the <pre> as a prop, into CodeBlock, out as a CSS class. Everything below is layout — how wide actually looks on the page.

Styling

Getting wide onto the component is one problem. Making the block visually wider is another. It depends entirely on your page layout: prose column width, whether you have a sidebar, and what that sidebar does as the reader scrolls.

The goal: let certain blocks extend past the prose column into empty space beside it — over a gap, and optionally over a sidebar column (a table of contents, related links, secondary nav, whatever you put there). As the reader scrolls, the wide block can pass over that sidebar track while the sidebar stays put or sticks in place.

On this site, the sidebar is a sticky table of contents. Wide code blocks overflow into the gap plus that sidebar column.

The wide adjustments

  • Paragraphs and ordinary code blocks keep max-width: var(--prose-max).
  • The page layout stays a simple two-column grid: article + sidebar, with the sidebar stretching to the full article height.
  • .code-wide blocks are wider than their siblings and allowed to overflow into the space beside the prose column.
.prose > * {
  max-width: var(--prose-max);
  min-width: 0;
}

.prose > .code-wide {
  width: calc(100% + var(--code-wide-extra, 0px));
  max-width: none;
  position: relative;
  z-index: 1;
}
  • --code-wide-extra is the gap plus the sidebar column width — only set when a sidebar is on screen
  • z-index lets the block pass over the sidebar track as you scroll
  • The article column needs overflow: visible so the extra width is not clipped

Set the variable on the layout when the sidebar is present:

@media (min-width: 1024px) {
  .layout:has(.sidebar) {
    --code-wide-extra: calc(var(--space-12) + 18rem);
    grid-template-columns: minmax(0, var(--layout-wide)) minmax(10rem, 18rem);
    column-gap: var(--space-12);
  }
}

Below the breakpoint, or on pages without a sidebar, --code-wide-extra falls back to 0px and wide blocks behave like normal blocks.

Conclusion

Ordinary fences are unchanged. Authors opt in with wide on the fence: one extra boolean from markdown meta through to a CSS class, via a small rehype plugin instead of a custom markdown tree walk.

Prerequisite: Create a CodeBlock Component for MDX.