Skip to main content

React Component Support

Mintlify MDX supports embedding custom React components directly in your documentation:
  • Import components from the snippets folder
  • Use React hooks (useState, useEffect, etc.)
  • Create interactive demos, calculators, and visualizations
  • Build reusable component libraries
snippets/ColorPicker.jsx
Using the Component
React components must be placed in the snippets folder and exported as named exports. You cannot import components from arbitrary MDX files.

Preventing the Double Flash

Mintlify pages render server-side first, then hydrate on the client. React components that modify layout or inject dynamic content cause a double flash: the server-rendered markup appears, disappears during hydration, then re-renders with client state. This creates a jarring flicker. The fix: render your component invisible on mount, then fade it in after hydration completes.
Every client-side React component should follow this three-part pattern:
  1. State gate: useState(false) tracks whether the component has mounted
  2. Mount trigger: useEffect(() => set(true), []) fires once after hydration
  3. Opacity transition: the wrapper starts at opacity: 0 and transitions to 1
snippets/MyComponent.jsx
Never use display: none or visibility: hidden for this purpose. Those properties trigger layout reflows when they change, causing the content to “jump” into place. The opacity + transition approach is GPU-accelerated and reflow-free.
Rules of thumb:
  • Only opacity and transform are safe for mount animations — they don’t trigger reflows
  • Always use ease-out timing — it feels responsive because the fast phase happens first
  • Keep transitions under 0.8s — longer feels sluggish, shorter can still flash
  • Use translateZ(0) to force GPU compositing, even if you’re not animating position

Loading External Scripts

Mintlify doesn’t support <script> tags directly in MDX. Instead, use Mintlify’s built-in load() function to fetch external libraries at runtime. Chain multiple load() calls with .then() to guarantee dependency order, and guard your initialization against Mintlify’s hydration timing.
snippets/AnimatedSection.jsx
The pattern has three key parts:
  1. Sequential loading: Chain .then() calls so dependent libraries (like ScrollTrigger) load after their prerequisites (GSAP core)
  2. Hydration-safe init: Check document.readyState first, then also set a setTimeout fallback — Mintlify’s hydration can delay DOM readiness beyond the browser’s load event
  3. Idempotency guard: Before the timeout fires, check whether initialization already ran (e.g., ScrollTrigger.getAll().length === 0) to avoid double-initializing
The load() function is a Mintlify global — it is not a standard browser API and won’t work outside of Mintlify. Don’t confuse it with dynamic import() or other module loaders.
Pin your CDN URLs to specific versions (e.g., gsap@3.14.1) rather than using @latest. Unpinned versions can break your docs without warning when the library ships a breaking change.