Files
mintlify__docs/customize/react-components.mdx
Ethan Palm 7662f8fb2a Feedback resolution 2026 06 08 (#6123)
* clarify MCP returns MD

* Fix doc issues surfaced by user feedback

- Fix claude mcp add --header argument order: the flag must come after
  the positional <name> and <url> args because --header is variadic and
  otherwise consumes everything that follows it, causing the "missing
  required argument 'name'" error users reported.
- Add tip to wrap iframes in Frame component to prevent overflow (user
  suggestion on image-embeds page).
- Fix broken anchor link in quickstart CLI tab: /cli/install has no
  #clone-your-repository section; replaced with inline git clone
  instructions and a correct link to /deploy/github.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Document wide-mode behavior for side panel

Wide mode hides the entire side panel (not just the TOC), including
Panel components and OpenAPI request/response examples. This was
undocumented and actively confused a user who couldn't understand why
their OpenAPI example panels disappeared on wide-mode pages.

- Fix the wide mode description in organize/pages.mdx (it previously
  said only the TOC was hidden, but ContentSideLayout.tsx returns null
  for wide/center/custom modes entirely)
- Add a Note to components/panel.mdx calling out that the side panel
  is absent on wide, center, and custom pages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Resolve user feedback: clarify path formats, load timing, and analytics config

- react-components: add Constraints section (hooks pre-injected, no npm, no default exports)
- posthog: fix default host from app.posthog.com to ph.mintlify.com (confirmed via source)
- plausible: add ParamField descriptions including server field explanation
- create/text: note that internal links require root-relative paths without file extensions
- create/image-embeds: clarify image paths are root-relative, relative paths unsupported
- create/redirects: show redirects as top-level field in full docs.json example
- customize/custom-scripts: note that custom JS runs after page is interactive, applies globally

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* 💅

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 17:21:31 -07:00

221 lines
7.6 KiB
Plaintext

---
title: "React"
description: "Build interactive and reusable elements with custom React components in your Mintlify documentation using JSX, state, and client-side logic."
keywords: ["React components", "interactive components", "JSX", "custom components"]
boost: 3
---
import { ColorGenerator } from "/snippets/color-generator.jsx";
Build interactive elements in your docs using [React components](https://react.dev) and [hooks](https://react.dev/reference/react/hooks) directly in MDX files.
## Inline components
Declare components directly in your MDX file:
export const Counter = () => {
const [count, setCount] = useState(0)
const increment = () => setCount(count + 1)
const decrement = () => setCount(count - 1)
return (
<div className="flex items-center justify-center">
<div className="flex items-center rounded-xl overflow-hidden border border-zinc-950/20 dark:border-white/20">
<button
onClick={decrement}
className="flex items-center justify-center h-8 w-8 text-zinc-950/80 dark:text-white/80 border-r border-zinc-950/20 dark:border-white/20"
aria-label="Decrease"
>
-
</button>
<div className="flex text-sm items-center justify-center h-8 px-6 text-zinc-950/80 dark:text-white/80 font-medium min-w-[4rem] text-center">
{count}
</div>
<button
onClick={increment}
className="flex items-center justify-center h-8 w-8 text-zinc-950/80 dark:text-white/80 border-l border-zinc-950/20 dark:border-white/20"
aria-label="Increase"
>
+
</button>
</div>
</div>
)
}
<Counter />
```mdx
export const Counter = () => {
const [count, setCount] = useState(0)
const increment = () => setCount(count + 1)
const decrement = () => setCount(count - 1)
return (
<div>
<button onClick={decrement}>-</button>
<span>{count}</span>
<button onClick={increment}>+</button>
</div>
)
}
<Counter />
```
## Constraints
React components in Mintlify run in a sandboxed MDX environment with the following constraints:
- **React hooks are pre-injected**: `useState`, `useEffect`, `useRef`, `useCallback`, `useMemo`, `useContext`, and `useReducer` are available without importing them.
- **No external npm packages**: Third-party packages (for example, `lodash`, `axios`, `date-fns`) cannot be imported. Use browser built-ins or write the logic inline.
- **No default exports**: Use named exports (`export const MyComponent = ...`). Default exports (`export default`) are not supported.
- **No cross-snippet imports**: Snippet files cannot import other snippet files. Import all dependencies directly in the parent MDX file.
- **No JSON imports**: Importing `.json` files is not supported.
## Import components
Component files must be in the `/snippets/` folder. Learn more about [reusable snippets](/create/reusable-snippets).
<Note>
Nested imports are not supported. Import all referenced components directly into the parent MDX file.
</Note>
Create a component file in `snippets/`:
```mdx /snippets/color-generator.jsx [expandable]
export const ColorGenerator = () => {
const [hue, setHue] = useState(180)
const [saturation, setSaturation] = useState(50)
const [lightness, setLightness] = useState(50)
const [colors, setColors] = useState([])
useEffect(() => {
const newColors = []
for (let i = 0; i < 5; i++) {
const l = Math.max(10, Math.min(90, lightness - 20 + i * 10))
newColors.push(`hsl(${hue}, ${saturation}%, ${l}%)`)
}
setColors(newColors)
}, [hue, saturation, lightness])
const copyToClipboard = (color) => {
navigator.clipboard
.writeText(color)
.then(() => {
console.log(`Copied ${color} to clipboard!`)
})
.catch((err) => {
console.error("Failed to copy: ", err)
})
}
return (
<div className="p-4 border dark:border-zinc-950/80 rounded-xl not-prose">
<div className="space-y-4">
<div className="space-y-2">
<label className="block text-sm text-zinc-950/70 dark:text-white/70">
Hue: {hue}°
<input
type="range"
min="0"
max="360"
value={hue}
onChange={(e) => setHue(Number.parseInt(e.target.value))}
className="w-full h-2 bg-zinc-950/20 rounded-lg appearance-none cursor-pointer dark:bg-white/20 mt-1"
style={{
background: `linear-gradient(to right,
hsl(0, ${saturation}%, ${lightness}%),
hsl(60, ${saturation}%, ${lightness}%),
hsl(120, ${saturation}%, ${lightness}%),
hsl(180, ${saturation}%, ${lightness}%),
hsl(240, ${saturation}%, ${lightness}%),
hsl(300, ${saturation}%, ${lightness}%),
hsl(360, ${saturation}%, ${lightness}%))`,
}}
/>
</label>
<label className="block text-sm text-zinc-950/70 dark:text-white/70">
Saturation: {saturation}%
<input
type="range"
min="0"
max="100"
value={saturation}
onChange={(e) => setSaturation(Number.parseInt(e.target.value))}
className="w-full h-2 bg-zinc-950/20 rounded-lg appearance-none cursor-pointer dark:bg-white/20 mt-1"
style={{
background: `linear-gradient(to right,
hsl(${hue}, 0%, ${lightness}%),
hsl(${hue}, 50%, ${lightness}%),
hsl(${hue}, 100%, ${lightness}%))`,
}}
/>
</label>
<label className="block text-sm text-zinc-950/70 dark:text-white/70">
Lightness: {lightness}%
<input
type="range"
min="0"
max="100"
value={lightness}
onChange={(e) => setLightness(Number.parseInt(e.target.value))}
className="w-full h-2 bg-zinc-950/20 rounded-lg appearance-none cursor-pointer dark:bg-white/20 mt-1"
style={{
background: `linear-gradient(to right,
hsl(${hue}, ${saturation}%, 0%),
hsl(${hue}, ${saturation}%, 50%),
hsl(${hue}, ${saturation}%, 100%))`,
}}
/>
</label>
</div>
<div className="flex space-x-1">
{colors.map((color, idx) => (
<div
key={idx}
className="h-16 rounded flex-1 cursor-pointer transition-transform hover:scale-105"
style={{ backgroundColor: color }}
title={`Click to copy: ${color}`}
onClick={() => copyToClipboard(color)}
/>
))}
</div>
<div className="text-sm font-mono text-zinc-950/70 dark:text-white/70">
<p>
Base color: hsl({hue}, {saturation}%, {lightness}%)
</p>
</div>
</div>
</div>
)
}
```
Then import and use it:
```mdx
import { ColorGenerator } from "/snippets/color-generator.jsx"
<ColorGenerator />
```
<ColorGenerator />
## Considerations
- **SEO**: Search engines may not fully index client-rendered dynamic content.
- **Initial load**: Visitors may see a flash before components render.
- **Accessibility**: Ensure screen readers announce dynamic content changes.
- **Optimize dependency arrays**: Only include necessary dependencies in `useEffect`.
- **Memoize expensive operations**: Use `useMemo` or `useCallback` where appropriate.
- **Reduce re-renders**: Break large components into smaller ones.
- **Lazy loading**: Lazy load complex components to improve initial page load.