Custom React components
Define and use inline React components in your MDX documentation
You can define custom React components directly in your MDX files, giving you full control over the presentation of your content.
Basic usage
Define a component using export const at the top of your MDX file:
export const Highlight = ({ children, color }) => (
<span style={{ backgroundColor: color, padding: '0.2em 0.4em', borderRadius: '4px' }}>
{children}
</span>
);
This text has a <Highlight color="#ffeb3b">yellow highlight</Highlight> in it.
Component names must use PascalCase (start with a capital letter).
Example: Hero card
Here's a more complex example with multiple props and Tailwind classes:
export const HeroCard = ({ title, description, href, icon }) => (
<a
href={href}
className="group block p-6 rounded-lg border border-gray-200 hover:border-blue-500 transition-colors"
>
<div className="flex items-center gap-3 mb-2">
<Icon icon={icon} />
<h3 className="font-semibold text-lg">{title}</h3>
</div>
<p className="text-gray-600">{description}</p>
</a>
);
<div className="grid grid-cols-2 gap-4">
<HeroCard
title="Getting started"
description="Learn the basics"
href="/quickstart"
icon="rocket"
/>
<HeroCard
title="Components"
description="Explore available components"
href="/components"
icon="puzzle-piece"
/>
</div>
Using built-in components
Your custom components have access to all of Jamdesk's built-in components. No imports needed:
export const FeatureCard = ({ title, children }) => (
<Card title={title} icon="star">
{children}
<Tip>This tip is inside a custom component!</Tip>
</Card>
);
<FeatureCard title="My Feature">
<Note>Notes work inside custom components too!</Note>
</FeatureCard>
Available built-in components:
- Callouts:
Note,Tip,Info,Warning,Check,Danger - Layout:
Card,CardGroup,Tabs,Tab,Accordion,Steps,Step,Columns,View - Media:
Frame,Icon,Badge,Tooltip - Code:
CodeGroup - API docs:
ParamField,ResponseField,Expandable
Limitations
- Arrow functions only: Use
export constsyntax. Theexport functionsyntax is not supported. - No imports: You cannot import external packages or other files
- No hooks: React hooks (
useState,useEffect,useRef, etc.) are not available. Components are server-rendered during build, and hooks only work client-side. For interactive components, use snippet files instead (see troubleshooting below). - Server-side only: Components render on the server; no client-side interactivity or event handlers
- Tailwind only: Use Tailwind CSS classes for styling; CSS-in-JS is not available
- Strict syntax: Syntax errors in inline components will fail the build with a clear error message
- PascalCase names: Component names must start with a capital letter and contain only letters and numbers (no underscores)
Need React hooks? Inline components cannot use useState, useEffect, or other hooks because they're rendered on the server during build. For interactive components that need state or effects, create a snippet file with the 'use client' directive (see troubleshooting section below).
Best practices
Inline components should be presentational. For complex logic, request a built-in component.
Ensure your components are accessible with proper HTML elements and ARIA attributes.
Preview your documentation locally to verify components render correctly in both light and dark modes.
Troubleshooting
Error: Expected component 'MyComponent' to be defined
Causes:
- Component name doesn't use PascalCase (must start with capital letter)
- Syntax error in the component definition
- Missing closing tag or parenthesis
Solution: Check that your export follows this pattern:
export const MyComponent = ({ prop }) => (
<div>{prop}</div>
);Error: Build fails mentioning Babel or JSX syntax
Causes:
- Invalid JSX syntax in your component
- Unclosed tags or brackets
- Using unsupported JavaScript features
Solution: Verify your JSX is valid. Common issues:
- All tags must be closed (
<img />not<img>) - Use
classNameinstead ofclass - Wrap multiple elements in a fragment
<>...</>or parent element
Warning: Inline component(s) override built-in: Note
Cause: Your inline component has the same name as a built-in component.
Solution: Rename your component to avoid conflicts:
// Instead of: export const Note = ...
export const CustomNote = ({ children }) => (
<div className="my-note">{children}</div>
);Issue: Tailwind classes aren't working
Causes:
- Using CSS-in-JS or inline style objects (limited support)
- Tailwind class not included in the build
Solution: Use standard Tailwind utility classes. For custom styles, use inline style prop with simple values:
export const Highlight = ({ children }) => (
<span style={{ backgroundColor: '#ffeb3b' }}>{children}</span>
);Issue: Component defined with export function doesn't render
Cause: Only export const arrow function syntax is supported.
Solution: Convert your function to arrow function syntax:
// Instead of:
export function MyComponent({ prop }) {
return <div>{prop}</div>;
}
// Use:
export const MyComponent = ({ prop }) => (
<div>{prop}</div>
);Issue: Component with underscores or special characters doesn't work
Cause: Component names must be valid PascalCase identifiers (letters and numbers only).
Solution: Use only letters and numbers in your component name:
// Instead of: export const Hero_Card = ...
// Instead of: export const my-component = ...
export const HeroCard = ({ title }) => (
<div>{title}</div>
);Error: ReferenceError: useState is not defined
Cause: React hooks are not available in inline components. Inline components are server-rendered during the build process, and hooks only work in client-side React components.
Solution: For interactive components that need state or effects, create a snippet file instead:
- Create a file in your
/snippetsdirectory (e.g.,/snippets/counter.tsx) - Add
'use client'at the top of the file - Import and use it in your MDX
'use client';
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}
<Counter />