React hooks and components for building custom themes and accessing site data, navigation, and component metadata.
Access site-wide data and metadata through React hooks.
/**
* Access site data context including configuration and metadata
* @returns Site data object with theme config and global settings
*/
function useSiteData(): {
themeConfig: IThemeConfig;
components: Record<string, AtomComponentAsset>;
locales: ILocalesConfig;
_2_level_nav_available: boolean;
[key: string]: any;
};
/**
* Get component asset metadata for current route
* @returns Asset metadata for components, functions, and other atoms
*/
function useAtomAssets(): {
components: Record<string, AtomComponentAsset>;
};
/**
* Get navigation data for the current locale
* @returns Navigation items array with titles, links, and hierarchy
*/
function useNavData(): INavItems;
/**
* Get sidebar data for the current route
* @returns Sidebar groups and items for current location
*/
function useSidebarData(): ISidebarGroup[];
/**
* Get full sidebar data including all routes
* @returns Complete sidebar structure for entire site
*/
function useFullSidebarData(): Record<string, ISidebarGroup[]>;
/**
* Get current route metadata including frontmatter and content
* @returns Route metadata with frontmatter, TOC, and text content
*/
function useRouteMeta(): IRouteMeta;
/**
* Get matched route information
* @returns Current matched route object
*/
function useMatchedRoute(): {
id: string;
path: string;
parentId?: string;
[key: string]: any;
};Usage:
import {
useSiteData,
useNavData,
useSidebarData,
useRouteMeta
} from "dumi/theme";
function CustomHeader() {
const { themeConfig } = useSiteData();
const nav = useNavData();
return (
<header>
<h1>{themeConfig.name}</h1>
<nav>
{nav.map(item => (
<a key={item.link} href={item.link}>
{item.title}
</a>
))}
</nav>
</header>
);
}
function CustomSidebar() {
const sidebar = useSidebarData();
const routeMeta = useRouteMeta();
return (
<aside>
<h2>{routeMeta.frontmatter.title}</h2>
{sidebar.map(group => (
<div key={group.title}>
<h3>{group.title}</h3>
{group.children.map(item => (
<a key={item.link} href={item.link}>
{item.title}
</a>
))}
</div>
))}
</aside>
);
}Manage user interface state and preferences.
/**
* Get current locale information
* @returns Current locale data
*/
function useLocale(): ILocale;
/**
* Get color scheme preference and switching utilities
* @returns Current color scheme and toggle function
*/
function usePrefersColor(): {
color: 'light' | 'dark' | 'auto';
setColor: (color: 'light' | 'dark' | 'auto') => void;
};
/**
* Get tab metadata for tabbed content pages
* @returns Current tab information and switching utilities
*/
function useTabMeta(): {
tab: string;
setTab: (tab: string) => void;
tabs: {
key: string;
title?: string;
titleIntlId?: string;
components: {
default: ReactComponentType;
Extra: ReactComponentType;
Action: ReactComponentType;
};
}[];
};Usage:
import { useLocale, usePrefersColor, useTabMeta } from "dumi/theme";
function LocaleDisplay() {
const locale = useLocale();
return (
<div>
<span>Current locale: {locale.name} ({locale.id})</span>
</div>
);
}
function ColorModeToggle() {
const { color, setColor } = usePrefersColor();
return (
<button onClick={() => setColor(color === 'dark' ? 'light' : 'dark')}>
{color === 'dark' ? 'โ๏ธ' : '๐'}
</button>
);
}Work with live demos and component rendering.
/**
* Get live demo compilation and rendering utilities
* @param id - Demo ID
* @param opts - Live demo options
* @returns Functions for compiling and rendering live code demos
*/
function useLiveDemo(id: string, opts?: {
containerRef?: RefObject<HTMLElement>;
iframe?: boolean;
}): {
/** Demo React node */
node: ReactNode;
/** Loading state */
loading: boolean;
/** Error state */
error: Error | null;
/** Set demo source code */
setSource: (source: Record<string, string>) => Promise<void>;
};
/**
* Get site search functionality and data
* @returns Search utilities and indexed content
*/
function useSiteSearch(): {
/** Search through site content */
search: (query: string) => Promise<Array<{
title: string;
content: string;
path: string;
}>>;
/** Current search results */
results: Array<any>;
/** Search loading state */
loading: boolean;
};Usage:
import { useLiveDemo, useSiteSearch } from "dumi/theme";
function LiveCodeEditor({ initialCode }: { initialCode: string }) {
const { renderDemo } = useLiveDemo();
const [code, setCode] = useState(initialCode);
const [demo, setDemo] = useState<React.ComponentType | null>(null);
useEffect(() => {
renderDemo(code, { filename: 'demo.tsx' })
.then(setDemo)
.catch(console.error);
}, [code, renderDemo]);
return (
<div>
<textarea
value={code}
onChange={(e) => setCode(e.target.value)}
/>
{demo && <demo />}
</div>
);
}Pre-built React components for common documentation needs.
/**
* Demo preview component with code display and live rendering
*/
interface DumiDemo {
(props: IPreviewerProps): JSX.Element;
}
interface IPreviewerProps {
/** Demo title */
title?: string;
/** Demo description */
description?: string;
/** Source filename */
filename?: string;
/** Render in iframe */
iframe?: boolean | number;
/** Debug mode flag */
debug?: boolean;
/** Show code by default */
defaultShowCode?: boolean;
/** Demo URL for standalone viewing */
demoUrl: string;
/** Disable padding */
compact?: boolean;
/** Apply transform for positioning */
transform?: boolean;
/** Background color */
background?: string;
/** Asset metadata */
asset: ExampleBlockAsset;
/** React children (the demo component) */
children: ReactNode;
}
/**
* Component for rendering component atoms/examples
*/
interface AtomRenderer {
(props: { atom: string }): JSX.Element;
}
/**
* Demo grid layout component
*/
interface DumiDemoGrid {
(props: {
items: IDemoData[];
cols?: number;
}): JSX.Element;
}
/**
* Page wrapper component
*/
interface DumiPage {
(props: {
children: ReactNode;
route: any;
}): JSX.Element;
}Usage:
import { DumiDemo, AtomRenderer, DumiDemoGrid } from "dumi/theme";
function CustomPreviewer(props: IPreviewerProps) {
return (
<div className="custom-demo">
<h3>{props.title}</h3>
<p>{props.description}</p>
<DumiDemo {...props} />
</div>
);
}
function ComponentShowcase({ atom }: { atom: string }) {
return (
<div className="showcase">
<AtomRenderer atom={atom} />
</div>
);
}Helper functions for external integrations and demo management.
/**
* Open current demo in CodeSandbox
* @param opts - Configuration for CodeSandbox integration
*/
function openCodeSandbox(opts: {
/** Demo code */
code: string;
/** Demo dependencies */
deps?: Record<string, string>;
/** Demo title */
title?: string;
/** Demo description */
description?: string;
}): void;
/**
* Open current demo in StackBlitz
* @param opts - Configuration for StackBlitz integration
*/
function openStackBlitz(opts: {
/** Demo code */
code: string;
/** Demo dependencies */
deps?: Record<string, string>;
/** Demo title */
title?: string;
/** Demo description */
description?: string;
}): void;Usage:
import { openCodeSandbox, openStackBlitz } from "dumi/theme";
function DemoActions({ code, deps }: {
code: string;
deps: Record<string, string>;
}) {
const handleOpenCodeSandbox = () => {
openCodeSandbox({
code,
deps,
title: "Component Demo",
description: "Generated from dumi",
});
};
const handleOpenStackBlitz = () => {
openStackBlitz({
code,
deps,
title: "Component Demo",
});
};
return (
<div>
<button onClick={handleOpenCodeSandbox}>
Open in CodeSandbox
</button>
<button onClick={handleOpenStackBlitz}>
Open in StackBlitz
</button>
</div>
);
}React-intl components for internationalized documentation.
// Re-exported from react-intl
function FormattedMessage(props: {
id: string;
defaultMessage?: string;
values?: Record<string, any>;
}): JSX.Element;
function FormattedDate(props: {
value: Date | number;
year?: 'numeric' | '2-digit';
month?: 'numeric' | '2-digit' | 'long' | 'short' | 'narrow';
day?: 'numeric' | '2-digit';
}): JSX.Element;
function FormattedNumber(props: {
value: number;
style?: 'decimal' | 'currency' | 'percent';
currency?: string;
}): JSX.Element;
function IntlProvider(props: {
locale: string;
messages: Record<string, string>;
children: ReactNode;
}): JSX.Element;
function useIntl(): {
formatMessage: (descriptor: { id: string; defaultMessage?: string }, values?: Record<string, any>) => string;
formatDate: (value: Date | number, options?: any) => string;
formatNumber: (value: number, options?: any) => string;
};Usage:
import { FormattedMessage, useIntl } from "dumi/theme";
function InternationalizedContent() {
const intl = useIntl();
return (
<div>
<h1>
<FormattedMessage
id="title"
defaultMessage="Welcome"
/>
</h1>
<p>
{intl.formatMessage(
{ id: "description", defaultMessage: "This is a demo" },
{ name: "Component" }
)}
</p>
</div>
);
}Create custom themes by organizing components and hooks:
// src/theme/components/Header.tsx
import { useSiteData, useNavData } from "dumi/theme";
export function Header() {
const { themeConfig } = useSiteData();
const nav = useNavData();
return (
<header>
<img src={themeConfig.logo} alt={themeConfig.name} />
<nav>
{nav.map(item => (
<a key={item.link} href={item.link}>
{item.title}
</a>
))}
</nav>
</header>
);
}Integrate with dumi's plugin system for advanced customization:
// Theme plugin that modifies theme data
export default (api: IApi) => {
api.modifyTheme((memo) => {
// Add custom components
memo.builtins.CustomComponent = require.resolve('./CustomComponent');
// Modify internationalization
memo.locales.en.messages.customKey = 'Custom Message';
return memo;
});
};