Tinyrack

MDX component map

Compile trusted MDX with Vite and render CommonMark and GFM through Tinyrack's React component map.

Set the trust boundary first

Use this integration for .mdx files reviewed with the application and compiled during the Vite build. MDX can contain JavaScript and JSX, so it executes with the application's privileges. Do not pass remote or user-authored MDX into this setup. Use a non-executable Markdown pipeline with an explicit sanitization policy for untrusted content. See the MDX security guidance before choosing a content source.

Install the build-time tools

Start with a React 19 and Vite application that already uses Tailwind CSS 4. Install Tinyrack at runtime and keep the compiler plugins in development dependencies.

pnpm add @tinyrack/ui react@^19 react-dom@^19
pnpm add -D @mdx-js/rollup @tailwindcss/vite @types/mdx @vitejs/plugin-react remark-gfm tailwindcss@^4 vite

Configure Vite

Run the MDX plugin in the pre phase so it converts .mdx before the React plugin. Include Markdown extensions in the React plugin and add remark-gfm for task lists, tables, deletion, autolinks, and footnotes. This path does not need @mdx-js/react or providerImportSource; the compiled component receives its map explicitly. The MDX Vite integration guide explains the plugin ordering.

import mdx from '@mdx-js/rollup';
import tailwindcss from '@tailwindcss/vite';
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';
import remarkGfm from 'remark-gfm';

export default defineConfig({
  plugins: [
    {
      enforce: 'pre',
      ...mdx({ remarkPlugins: [remarkGfm] }),
    },
    react({ include: /\.(?:js|jsx|ts|tsx|md|mdx)$/ }),
    tailwindcss(),
  ],
});

Load the Tailwind entry first, followed by the Tinyrack core, MDX, and component styles used by the map.

@import 'tailwindcss';
@import '@tinyrack/ui/core.css';
@import '@tinyrack/ui/mdx.css';
@import '@tinyrack/ui/components/code.css';
@import '@tinyrack/ui/components/code-block.css';
@import '@tinyrack/ui/components/link.css';
@import '@tinyrack/ui/components/table.css';

Render a trusted MDX file

Import the compiled .mdx file and pass tinyrackMdxComponents through its components prop. Content is a React component produced at build time, not an HTML string passed to a runtime renderer.

import Content from './content.mdx';
import { tinyrackMdxComponents } from '@tinyrack/ui/mdx';
import './app.css';

export function MdxArticle() {
  return <Content components={tinyrackMdxComponents} />;
}

Preview a real MDX file

The preview imports an actual locale-specific .mdx file. The sample intentionally uses every CommonMark and GFM element supported by the Tinyrack map, including headings, prose, inline formatting, lists, images, blockquotes, hard breaks, horizontal rules, task lists, tables, fenced code, and footnotes.

Compiled MDX output permalink

Compare the authored MDX with the React composition used inside this documentation page.

Choose the page landmark

The default wrapper in tinyrackMdxComponents renders a <main className="tr-mdx">. Use that default when the MDX document owns the page's primary content. If the application already renders a main landmark, create a new map and override wrapper with an article. Keep the tr-mdx class so the shared prose styles still apply.

import type { ComponentPropsWithoutRef } from 'react';
import Content from './content.mdx';
import { createTinyrackMdxComponents } from '@tinyrack/ui/mdx';
import './app.css';

function ArticleWrapper({
  children,
  className,
  ...props
}: ComponentPropsWithoutRef<'article'>) {
  return (
    <article
      {...props}
      className={['tr-mdx', className].filter(Boolean).join(' ')}
    >
      {children}
    </article>
  );
}

const articleComponents = createTinyrackMdxComponents({
  components: { wrapper: ArticleWrapper },
});

export function ProductPage() {
  return (
    <main>
      <Content components={articleComponents} />
    </main>
  );
}

Customize the map

createTinyrackMdxComponents({ components }) returns a new map containing every default key and then applies the supplied overrides. Use it to replace a heading, link, wrapper, or another mapped element without mutating tinyrackMdxComponents. Preserve semantic HTML, forwarded attributes, accessible names, and the classes required by the replacement's styling contract.

Contract and limits

MDX outputDefault ownerImportant behavior
Document wrappermain.tr-mdxOverride with article.tr-mdx inside an existing main.
Inline and fenced codeTRCode, TRCodeBlockFenced code uses progressive Shiki highlighting.
LinksTRLinkLink semantics and destination remain authored in MDX.
TablesTRTable.RootThe table container owns local horizontal overflow.
Task-list inputSemantic disabled checkboxThe authored checked state is preserved.
Other proseSemantic React elementsHeadings, lists, quotes, images, and footnotes receive shared MDX classes.

This integration does not fetch, sandbox, or compile MDX at runtime. It also does not define framework-specific setup for Next.js or Astro.

Verify the integration

  1. Run the production Vite build and confirm that the imported .mdx module compiles.
  2. Inspect the page for one primary main landmark. Use the article override when another main already exists.
  3. Confirm that task-list checkboxes are disabled, links reach their intended destinations, and the table scrolls within its own container on a narrow viewport.
  4. Check inline and fenced code in light and dark themes, including the state before progressive highlighting finishes.