Skip to content

Mantine reference

@ai-markdown/react-mantine adds Mantine presentation to the React renderer: theme-aware typography, expandable highlighted code, source-preserving JSON formatting, and Mermaid diagrams. Its MantineAIMarkdown wrapper accepts the React adapter’s props and adds one codeBlock behavior group.

Parsing, URL policy, metadata, and cross-chunk references remain engine/React adapter responsibilities. The integration supplies default slots and a pre renderer; caller overrides take precedence. Set up the stylesheet imports and both providers in the quick start before using the code-block features. If you replace pre, your component takes over the formatting, copy, highlighting, and diagram behavior described here.

Upgrading from 1.x? v2.0.0 removes the 1.x object-based config channel — the Mantine code-block options move to a flat codeBlock prop, and the render-state hook is replaced by narrow hooks plus useMantineCodeBlockOptions(). See the migration guide.

  • Mantine typography — markdown content is wrapped in Mantine’s <Typography> so it inherits the active theme’s font family, line height, and color tokens
  • Syntax highlighting — code blocks render via @mantine/code-highlight with the adapter you provide (highlight.js or Shiki), with language-labelled tabs, expand/collapse, and optional language detection for unlabelled blocks through @ai-markdown/code-language-detector
  • Mermaid diagrams — fenced mermaid code blocks render as interactive SVG diagrams with dark/light theme support, source toggle, copy, and open-in-new-window
  • JSON pretty-print — fenced json code blocks are validated and formatted with 2-space indent while retaining numeric tokens, duplicate keys, and key order; string values that are themselves JSON documents (an object or array — the tool-call transcript shape) are expanded too, primitive-looking strings ("true", "123") are left as written
  • Automatic color scheme — follows the active MantineProvider; under auto the system preference is read on the first client frame (server output is light), and the result is forwarded to the core renderer when no explicit colorScheme prop is supplied
  • Mantine-scoped CSS — extra-styles wrapper overrides Mantine spacing/font-size custom properties to use relative em units, giving consistent scaling at any base font size

All React adapter features (GFM, LaTeX math, CJK support, streaming, metadata context, content preprocessors, custom components, cross-chunk coordination via <AIMarkdownDocuments>) are inherited unchanged from @ai-markdown/react. See the React reference for the base API.

PackageRoleVersion policy
@ai-markdown/coreFramework-independent sessions, block planning, contributions and smooth coordinationRelease train; exact engine dependency
@ai-markdown/reactThe React renderer — <AIMarkdown>, <AIMarkdownSmoothStream>, <AIMarkdownDocuments>, hooks, providersRelease train
@ai-markdown/vueVue 3.5 renderer — components, scoped slots, SSR/hydration and smooth composablesRelease train; exact core and engine dependencies
@ai-markdown/react-mantineMantine UI bindings — themed typography, code-highlight tabs, Mermaid, color-scheme wiringRelease train; compatible React 3.x peer
@ai-markdown/engineFramework-agnostic engine — incremental parsing, LaTeX preprocessing, plugin pipeline, cross-chunk registryRelease train; pinned exactly by shared core and adapters
@ai-markdown/remark-mark-highlightremark plugin for ==mark== highlight syntaxIndependent semver
@ai-markdown/code-language-detectorHeuristic language detection for unlabelled code blocks, and fence language names mapped to Shiki or highlight.js names; a regular dependency of this packageIndependent semver
Mantine@mantine/core ^9 and @mantine/code-highlight ^9 (peer dependencies)
highlight.js^11.11.2 (optional peer; never imported by the package — needed only for Mantine’s highlight.js adapter)
React^19.0.0
Node^20.19.0 || >=22.12.0 (engines.node)
Module formatsESM and CJS with types; the compiled stylesheet is exported as @ai-markdown/react-mantine/styles.css
React adapterPeer range below; upgrade both packages together
Terminal window
# npm
npm install @ai-markdown/react-mantine @ai-markdown/react
# pnpm
pnpm add @ai-markdown/react-mantine @ai-markdown/react
# yarn
yarn add @ai-markdown/react-mantine @ai-markdown/react

The commands above add the integration and its React peer. For a new React application, also install the UI peers and KaTeX for the math example:

Terminal window
pnpm add react@^19 react-dom@^19 @mantine/core@^9 @mantine/code-highlight@^9 highlight.js@^11.11.2 katex

Mantine uses the React adapter and does not depend directly on shared core. Vue applications use the Vue adapter; this integration provides React components only.

{
"react": "^19.0.0",
"react-dom": "^19.0.0",
"@ai-markdown/react": "^3.5.0",
"@mantine/core": "^9.0.0",
"@mantine/code-highlight": "^9.0.0",
"highlight.js": "^11.11.2"
}

highlight.js is declared optional (peerDependenciesMeta). The package contains no import of it, so a build without it succeeds; install it only for Mantine’s highlight.js adapter. Language auto-detection does not use it: it runs on @ai-markdown/code-language-detector, which is a regular dependency and installs with the package.

Import the required stylesheets in your application entry point:

// Mantine core styles (required)
import '@mantine/core/styles.css';
// Mantine code highlight styles (required for code blocks)
import '@mantine/code-highlight/styles.css';
// Mantine AI Markdown styles (required for extra styles + Mermaid)
import '@ai-markdown/react-mantine/styles.css';
// KaTeX styles (required for LaTeX math rendering)
import 'katex/dist/katex.min.css';
import { MantineProvider } from '@mantine/core';
import { CodeHighlightAdapterProvider, createHighlightJsAdapter } from '@mantine/code-highlight';
import hljs from 'highlight.js';
import MantineAIMarkdown from '@ai-markdown/react-mantine';
const highlightJsAdapter = createHighlightJsAdapter(hljs);
function App() {
return (
<MantineProvider>
<CodeHighlightAdapterProvider adapter={highlightJsAdapter}>
<MantineAIMarkdown content="Hello **world**! Math: $E = mc^2$" />
</CodeHighlightAdapterProvider>
</MantineProvider>
);
}
function StreamingChat({ content, isStreaming }: { content: string; isStreaming: boolean }) {
return <MantineAIMarkdown content={content} streaming={isStreaming} />;
}

MantineAIMarkdownProps<TMetadata> extends AIMarkdownProps<TMetadata> — every React adapter prop (enginePlugins, blockMemo, incrementalParse, preserveOrphanReferences, streamingCursor, …) is supported, plus the Mantine-specific codeBlock prop. The table below lists the props with a Mantine-specific default override or addition (props not listed here inherit React adapter defaults unchanged — see the React props reference).

PropTypeDefaultDescription
colorSchemeAIMarkdownColorSchemeAuto-detectedColor scheme. When omitted, follows the MantineProvider: its light/dark, or under auto the system preference (light on the server).
customComponentsAIMarkdownCustomComponentsMantine defaultsComponent overrides, merged with Mantine’s built-in <pre> handler. Caller overrides take precedence — including pre here disables Mantine’s code-block features.
TypographyAIMarkdownTypographyComponentMantineAIMarkdownTypographyTypography wrapper component.
ExtraStylesAIMarkdownExtraStylesComponentMantineAIMDefaultExtraStylesExtra style wrapper rendered between typography and content.
codeBlockPartial<MantineCodeBlockOptions>defaultMantineCodeBlockOptionsCode-block behavior group (Mantine-specific). The group value replaces atomically; omitted fields resolve to the shipped defaults inside useMantineCodeBlockOptions(). null counts as absent.

The codeBlock prop transports a partial behavior group. An absent or null group contributes no codeBlock key, so an outer AIMarkdownBehaviorsProvider can supply it. A present group replaces an outer group atomically: { defaultExpanded: false } does not inherit the outer group’s other fields. The narrow hook fills omitted fields from package defaults.

codeBlock (Partial<MantineCodeBlockOptions>)

Section titled “codeBlock (Partial<MantineCodeBlockOptions>)”
FieldTypeDefaultBehavior
defaultExpandedbooleantrueInitial expanded state; false starts long blocks collapsed with an expand action
autoDetectUnknownLanguagebooleantrueIdentify an unannotated block’s language with @ai-markdown/code-language-detector, synchronously during render (server rendering included). A block the detector abstains on stays plaintext labelled “unknown”; the detector never overrides an explicit fence language
languageFormatMantineLanguageFormatMantineLanguageFormat.HighlightJsThe names languages are handed to the highlighter in; match it to the adapter in your CodeHighlightAdapterProvider (HighlightJs or Shiki). Applies to a language written on the fence and to a detected one: under highlight.js ```objc is highlighted as objectivec and ```txt as plaintext, and under Shiki ```Makefile as make. The tab label keeps the name as written (lower-cased), or the detected language’s own name. An unrecognised value falls back to the default
formatJsonbooleantrueFormat valid JSON for display while preserving numeric tokens, duplicate keys, and order
expandNestedJsonbooleantrueWhile formatting, expand string values that contain JSON objects or arrays
highlightIntervalMsnumber50Coalesce appended code display updates during streaming; zero displays every update
mermaidIntervalMsnumber300Shortest time between two mermaid render attempts of one block while streaming; the final source renders once streaming ends; zero attempts every update
labelsLabelsOption<MantineCodeBlockLabels>EnglishText of the code block: a partial object, or a function returning one that is called on every render. See text and translation
mermaidLabelsLabelsOption<MantineMermaidLabels>EnglishText of the Mermaid block, in the same form

Explicit undefined fields retain their shipped defaults. Both intervals must be finite and non-negative; invalid values fall back to the defaults (50 ms and 300 ms). Do not assume that null is a supported value for individual fields merely because null at the group boundary counts as absent: no individual field accepts null.

<MantineAIMarkdown content={markdown} codeBlock={{ defaultExpanded: false }} />

The omitted options resolve to the defaults in the table. To keep JSON structure exactly as written apart from whitespace, set expandNestedJson: false; to display the original JSON text, set formatJson: false. Neither setting changes what the copy button copies.

For a stable reusable fragment, use the widened factory:

import { defineMantineBehaviors } from '@ai-markdown/react-mantine';
const BEHAVIORS = defineMantineBehaviors({
blockMemo: true,
codeBlock: { defaultExpanded: false, expandNestedJson: false },
});
<MantineAIMarkdown content={markdown} {...BEHAVIORS} streaming={isStreaming} />;

The factory provides types and shallow freezing, not default resolution or recursive merging. Runtime props placed after a spread win in ordinary JSX order. Behavior-group defaults are applied only by useMantineCodeBlockOptions(), so custom code renderers should read that hook rather than reproduce the table locally.

Narrow hook for the codeBlock behavior group — the single place the group’s type assertion and defaults live. Returns ResolvedMantineCodeBlockOptions: the caller-passed group merged over defaultMantineCodeBlockOptions, with labels and mermaidLabels passed through as given (each component merges them over its own defaults when it renders).

import { useMantineCodeBlockOptions } from '@ai-markdown/react-mantine';
function MyCodeBlock() {
const { defaultExpanded, autoDetectUnknownLanguage } = useMantineCodeBlockOptions();
// ...
}

For everything else (streaming state, theme, document ids, core behavior switches), use the React adapter’s narrow hooks directly — useAIMarkdownState(), useAIMarkdownTheme(), useAIMarkdownDocument(), useAIMarkdownBehaviors() — or the aggregate useAIMarkdown() for low-frequency components. See the React hooks reference.

import { useAIMarkdownState, useAIMarkdownTheme } from '@ai-markdown/react';
import { useMantineCodeBlockOptions } from '@ai-markdown/react-mantine';
function MyCodeBlock() {
const { streaming } = useAIMarkdownState();
const { colorScheme } = useAIMarkdownTheme();
const { defaultExpanded } = useMantineCodeBlockOptions();
// ...
}

Typed wrapper around the core useAIMarkdownMetadata, defaulting TMetadata to MantineAIMarkdownMetadata. Metadata lives in a separate React context from render state, so metadata updates do not cause re-renders in components that only consume render state.

import { useMantineAIMarkdownMetadata } from '@ai-markdown/react-mantine';
function MyComponent() {
const metadata = useMantineAIMarkdownMetadata<{ messageId: string }>();
// ...
}

Default typography wrapper. Renders Mantine’s <Typography> with w="100%" and fz={fontSize}, so all rendered markdown inherits the active theme’s font family, line height, and color tokens. Receives the style prop carrying the React adapter’s CSS custom properties (--aim-font-size-root) and forwards it onto the Mantine root.

Replace it via the Typography prop when you need different theming, but consider extending rather than replacing — the wrapper is intentionally minimal.

Default ExtraStyles wrapper. Renders a <div className="aim-mantine-extra-styles"> that activates the package’s scoped CSS overrides:

  • Mantine spacing and font-size CSS custom properties switched to relative em units (consistent scaling at any base font size)
  • Heading, list, paragraph, blockquote, and inline-code spacing tuned for AI-generated markdown
  • Definition list layout

Activated by importing @ai-markdown/react-mantine/styles.css in your app entry. Pass a custom ExtraStyles prop to bypass these defaults.

See Code Block Rendering for the rendering behavior and configuration examples.

See Mermaid Diagrams for the rendering behavior and configuration examples.

MantineAIMarkdown resolves its color scheme in this order:

  1. Explicit non-null colorScheme prop (undefined uses the wrapper default; runtime null reaches the React adapter’s fallback)
  2. The active MantineProvider’s scheme: light or dark as set, or under auto the system prefers-color-scheme query, read on the first client frame through useSyncExternalStore (server output is light; hydration re-renders to the client value without a mismatch). Mantine’s own useComputedColorScheme is not used because it seeds auto with its default and reads the query in an effect, which committed a light frame first on dark systems.
// Follows Mantine's color scheme automatically
<MantineAIMarkdown content={markdown} />
// Explicit override
<MantineAIMarkdown content={markdown} colorScheme="dark" />

The resolved color scheme is forwarded to:

  • The core <AIMarkdown> for typography theming
  • Mermaid diagram rendering (dark / base theme selection)
  • The extra-styles wrapper for color-aware CSS

Caller-provided customComponents are merged on top of the Mantine defaults; caller overrides take precedence:

import MantineAIMarkdown from '@ai-markdown/react-mantine';
import type { AIMarkdownCustomComponents } from '@ai-markdown/react';
const customComponents: AIMarkdownCustomComponents = {
a: ({ href, children }) => (
<a href={href} target="_blank" rel="noopener noreferrer">
{children}
</a>
),
img: ({ src, alt }) => <img src={src} alt={alt} loading="lazy" />,
};
<MantineAIMarkdown content={markdown} customComponents={customComponents} />;

To override the default <pre> handler (and lose built-in code highlighting, Mermaid, and JSON pretty-print support), include pre in your custom components.

<MantineAIMarkdown> participates in cross-chunk coordination identically to <AIMarkdown>. Wrap multiple chunks in <AIMarkdownDocuments> (from @ai-markdown/react) and share documentId to coordinate footnotes, link references, and image references across chunks:

import { AIMarkdownDocuments } from '@ai-markdown/react';
import MantineAIMarkdown from '@ai-markdown/react-mantine';
<AIMarkdownDocuments>
{message.chunks.map((c, i) => (
<MantineAIMarkdown key={i} content={c} documentId={message.id} />
))}
</AIMarkdownDocuments>;

See the React reference’s cross-chunk section for the full <AIMarkdownDocuments> API and useDocumentRegistry hook.

Typewriter pacing composes with <MantineAIMarkdown> through the React adapter’s useSmoothStream hook — its result is props-shaped, so it spreads straight in:

import { useSmoothStream } from '@ai-markdown/react';
import MantineAIMarkdown from '@ai-markdown/react-mantine';
function ChatMessage({ markdown, pending }: { markdown: string; pending: boolean }) {
const smooth = useSmoothStream({ content: markdown, streaming: pending, pacing: 'balanced' });
return <MantineAIMarkdown {...smooth} />;
}

For multi-chunk documents under <AIMarkdownDocuments>, swap in useDocumentSmoothStream and chunks sharing a documentId reveal turn-by-turn (one typewriter, one cursor). Pass the SAME id to the hook and the component — the hook can’t cross-check the two:

import { useDocumentSmoothStream } from '@ai-markdown/react';
function ChatChunk({ id, markdown, pending }: { id: string; markdown: string; pending: boolean }) {
const smooth = useDocumentSmoothStream({ documentId: id, content: markdown, streaming: pending });
return <MantineAIMarkdown {...smooth} documentId={id} />;
}

See apps/docs/content/guides/smooth-streaming.md for the pacing model, presets, and footguns.

<MantineAIMarkdown>
└─ wraps <AIMarkdown> with Mantine defaults:
Typography = MantineAIMarkdownTypography (Mantine <Typography>)
ExtraStyles = MantineAIMDefaultExtraStyles (aim-mantine-extra-styles scope)
customComponents.pre = MantineAIMPreCode (CodeHighlight + Mermaid + JSON pretty-print)
colorScheme = provider scheme / system query (when not overridden)

Caller-provided Typography, ExtraStyles, and customComponents props override the Mantine defaults at their respective slots. Inside the wrapped <AIMarkdown>, the rest of the render pipeline (the five per-system contexts, content preprocessors, remark/rehype plugin chain) is identical to the standalone React adapter — see the React architecture overview.

  • MantineAIMarkdown — the main component (memoized)
  • MantineAIMarkdownTypography — Mantine-themed typography wrapper
  • MantineAIMDefaultExtraStyles — default extra styles wrapper with Mantine CSS scoping
  • MantineAIMarkdownProps
  • MantineAIMarkdownMetadata
  • MantineCodeBlockOptions — the codeBlock group shape
  • ResolvedMantineCodeBlockOptions — what useMantineCodeBlockOptions() returns
  • MantineCodeBlockLabels, MantineMermaidLabels — text of the code block and of the Mermaid block
  • MantineBehaviorProps — input type of defineMantineBehaviors
  • defaultMantineCodeBlockOptions — shipped defaults of the codeBlock behavior group (frozen)
  • defaultMantineCodeBlockLabels, defaultMantineMermaidLabels — the English text (frozen)
  • MantineLanguageFormat — the value of codeBlock.languageFormat: HighlightJs ('highlight-js', the default) or Shiki ('shiki')
  • preloadMantineCodeAssets(): Promise<void> — starts the lazy Mermaid import; takes no arguments; idempotent, with renderer fallback if loading fails
  • defineMantineBehaviors() — widened behaviors factory (core behavior fields + codeBlock); identity + types + Object.freeze, zero logic
  • useMantineCodeBlockOptions() — typed access to the codeBlock group with defaults applied
  • useMantineAIMarkdownMetadata<TMetadata>() — typed metadata access

Everything below applies unchanged through <MantineAIMarkdown>; the mantine-specific parts are the sections above.

GuideWhat it covers
Streaming & performanceBlock memoization, incremental (prefix-freeze) parsing, what to pass while tokens arrive
Smooth streaming<AIMarkdownSmoothStream> typewriter reveal, pacing presets, document turn-taking
Streaming cursorThe overlay cursor that tracks the streaming tail
Cross-chunk coordination<AIMarkdownDocuments>, footnotes and link references across chunks, the registry
URL sanitization & custom schemesThe two-gate model, urlTransform, extendSanitizeSchema
Custom components · Custom typography · Design tokensSwapping renderers, theming, the --aim-* variables
CJK typographyLine breaking, spacing, pangu
Metadata context · TypeScript genericsPassing typed metadata to custom components
Content preprocessorsRewriting the source before it parses
Extending via subpackageBuilding your own UI-kit binding (the mantine package is the reference)
Architecture · BenchmarkHow the packages fit together, measured numbers
Migrating to v2 · Release highlightsOld → new API mapping, what changed per version

For base features, configuration options, content preprocessors, TypeScript generics, and architecture details, see the React reference.

Streaming code: source, display, and asynchronous work

Section titled “Streaming code: source, display, and asynchronous work”

See Streaming code: source, display, and asynchronous work for the rendering behavior and configuration examples.

Check light and dark schemes, a known language, an unannotated block, JSON with a large numeric literal, a nested JSON string, and a Mermaid fence that is incomplete before becoming valid. Copy each source and compare its whitespace with the original. Then replace a block with different content of the same length, complete a stream during a pending highlight interval, and change the theme after a diagram has rendered.

For custom wrappers, test an outer behavior provider both with an absent codeBlock prop and a present partial group. For multiple logical chunks, use the same React AIMarkdownDocuments wrapper and explicit document ids; no Mantine-specific registry exists. Smooth presentation composes through the React adapter’s hooks, whose returned streaming state includes the reveal drain.

See src/MantineAIMarkdown.tsx for wrapper precedence and src/defs.tsx for the authoritative group defaults. This reference describes the presentation layer; the React reference remains the reference for inherited parsing and coordination behavior.

MIT