@mintlify/components includes built-in dark mode support with automatic theme detection. Components automatically adapt their appearance based on a dark class on the root HTML element.
How Dark Mode Works
The library uses the .dark class selector pattern:
Light mode: No class on <html>
Dark mode: <html class="dark">
Components detect theme changes automatically and update their styling accordingly using Tailwind’s dark: variant.
Basic Setup
Add dark class toggle
Implement a way to toggle the dark class on your root HTML element: import { useEffect , useState } from 'react' ;
function ThemeToggle () {
const [ isDark , setIsDark ] = useState ( false );
useEffect (() => {
const isDarkMode = document . documentElement . classList . contains ( 'dark' );
setIsDark ( isDarkMode );
}, []);
const toggleTheme = () => {
document . documentElement . classList . toggle ( 'dark' );
setIsDark ( ! isDark );
};
return (
< button onClick = { toggleTheme } >
{ isDark ? 'Light Mode' : 'Dark Mode' }
</ button >
);
}
Components update automatically
All @mintlify/components will automatically respond to the theme change: import { Callout , Card , CodeBlock } from '@mintlify/components' ;
function App () {
return (
<>
< ThemeToggle />
< Callout variant = "info" >
This callout adapts to dark mode automatically
</ Callout >
< Card title = "Dark Mode" icon = "moon" >
Cards also adjust their colors
</ Card >
</>
);
}
Using next-themes
For Next.js applications, next-themes is the recommended approach for handling theme management.
Add ThemeProvider
Wrap your application with the ThemeProvider: import { ThemeProvider } from 'next-themes' ;
export default function RootLayout ({
children ,
} : {
children : React . ReactNode ;
}) {
return (
< html lang = "en" suppressHydrationWarning >
< body >
< ThemeProvider attribute = "class" defaultTheme = "system" >
{ children }
</ ThemeProvider >
</ body >
</ html >
);
}
Create theme toggle
components/theme-toggle.tsx
'use client' ;
import { useTheme } from 'next-themes' ;
import { useEffect , useState } from 'react' ;
export function ThemeToggle () {
const [ mounted , setMounted ] = useState ( false );
const { theme , setTheme } = useTheme ();
useEffect (() => setMounted ( true ), []);
if ( ! mounted ) return null ;
return (
< button
onClick = { () => setTheme ( theme === 'dark' ? 'light' : 'dark' ) }
className = "px-4 py-2 rounded-lg border"
>
{ theme === 'dark' ? 'Light Mode' : 'Dark Mode' }
</ button >
);
}
Detecting Theme in Components
You can create a custom hook to detect theme changes by observing the dark class on the document element:
import { useEffect , useState } from 'react' ;
const useIsDarkTheme = () => {
const [ isDarkTheme , setIsDarkTheme ] = useState ( false );
useEffect (() => {
const checkTheme = () => {
setIsDarkTheme ( document . documentElement . classList . contains ( 'dark' ));
};
// Check initial state
checkTheme ();
// Watch for theme changes
const observer = new MutationObserver ( checkTheme );
observer . observe ( document . documentElement , {
attributes: true ,
attributeFilter: [ 'class' ],
});
return () => observer . disconnect ();
}, []);
return { isDarkTheme };
};
// Use in your components
function ThemeAwareComponent () {
const { isDarkTheme } = useIsDarkTheme ();
return (
< div >
Current theme: { isDarkTheme ? 'dark' : 'light' }
</ div >
);
}
This hook uses a MutationObserver to watch for class changes on the HTML element, allowing your components to react to theme changes in real-time.
Customizing Dark Mode Colors
Tailwind v4
Use CSS variables in your @theme block:
@import "@mintlify/components/styles.css" ;
@import "tailwindcss" ;
@theme {
/* Light mode colors */
--color-primary: #3b82f6 ;
--color-primary-light: #dbeafe;
/* Dark mode colors */
--color-primary-dark: #1e40af ;
--color-background-dark: #0f172a ;
--color-tooltip-foreground: #ffffff;
}
Tailwind v3
Extend dark mode colors in your config:
module . exports = {
darkMode: 'class' , // Required!
theme: {
extend: {
colors: {
primary: {
DEFAULT: '#3b82f6' ,
light: '#dbeafe' ,
dark: '#1e40af' ,
},
},
},
},
}
For Tailwind v3, you must set darkMode: 'class' in your config for dark mode to work with the components.
Component-Specific Dark Mode
Callout Component
Callouts automatically adapt their colors in dark mode using predefined color schemes:
import { Callout } from '@mintlify/components' ;
// Info variant - from source/packages/components/src/components/callout/callout.tsx:36-43
// Light: border-stone-200 bg-stone-50
// Dark: border-stone-700 bg-white/10
< Callout variant = "info" >
This note looks great in both themes
</ Callout >
// Warning variant - from source/packages/components/src/components/callout/callout.tsx:44-50
// Light: border-yellow-200 bg-yellow-50 text-yellow-800
// Dark: border-yellow-900 bg-yellow-600/20 text-yellow-300
< Callout variant = "warning" >
Warnings use yellow tones that adapt automatically
</ Callout >
// Custom colors also work in dark mode
< Callout variant = "custom" color = "#8b5cf6" >
Custom color with automatic dark mode adjustment
</ Callout >
Accordion Component
Accordions use adaptive borders and backgrounds:
import { Accordion } from '@mintlify/components' ;
// From source/packages/components/src/components/accordion/accordion.tsx:204-207
// Light: border-stone-200/70 bg-white
// Dark: border-white/10 bg-[#0b0c0e]
< Accordion title = "Getting Started" >
Accordion content adapts to dark mode
</ Accordion >
Badge Component
Badges use CSS variables for dynamic color adaptation:
import { Badge } from '@mintlify/components' ;
// From source/packages/components/src/components/badge/badge.tsx:31-32
// Blue badge uses:
// Light: bg-[#E3EAFD] text-[#133A9A]
// Dark: bg-[#07296A] text-[#7196F4]
< Badge color = "blue" > TypeScript </ Badge >
// Green badge uses:
// Light: bg-[#D1FAE4] text-[#166E3F]
// Dark: bg-[#0F4C2C] text-[#6AE1A1]
< Badge color = "green" > Success </ Badge >
Code Blocks
Code blocks use Shiki for syntax highlighting with dual themes:
import { CodeBlock } from '@mintlify/components' ;
< CodeBlock
code = { `console.log('Hello World');` }
language = "javascript"
/>
The code block automatically switches between light and dark syntax themes based on the current mode.
Custom Dark Mode Styles
For your own components, use Tailwind’s dark: variant:
< div className = "bg-white dark:bg-stone-950 text-black dark:text-white" >
Responsive to theme
</ div >
< div className = "border border-stone-200 dark:border-stone-700" >
Adaptive borders
</ div >
< div className = "shadow-lg dark:shadow-none" >
Different shadows per theme
</ div >
CSS Variables for Dark Mode
The library uses several CSS variables that adapt to dark mode:
/* Code block fade overlay */
--fade-color-light: /* Light mode background */
--fade-color-dark: /* Dark mode background */
--background-light: /* Default light background */
--background-dark: /* Default dark background */
/* Shiki syntax highlighting */
--shiki-light: /* Light theme text color */
--shiki-dark: /* Dark theme text color */
--shiki-light-bg: /* Light theme background */
--shiki-dark-bg: /* Dark theme background */
These variables are automatically managed by the components.
Persisting Theme Preference
localStorage
System Preference
import { useEffect , useState } from 'react' ;
function useTheme () {
const [ theme , setTheme ] = useState (() => {
if ( typeof window !== 'undefined' ) {
return localStorage . getItem ( 'theme' ) || 'light' ;
}
return 'light' ;
});
useEffect (() => {
const root = document . documentElement ;
if ( theme === 'dark' ) {
root . classList . add ( 'dark' );
} else {
root . classList . remove ( 'dark' );
}
localStorage . setItem ( 'theme' , theme );
}, [ theme ]);
return { theme , setTheme };
}
import { useEffect , useState } from 'react' ;
function useSystemTheme () {
const [ theme , setTheme ] = useState ( 'light' );
useEffect (() => {
const mediaQuery = window . matchMedia ( '(prefers-color-scheme: dark)' );
const handleChange = ( e ) => {
setTheme ( e . matches ? 'dark' : 'light' );
document . documentElement . classList . toggle ( 'dark' , e . matches );
};
setTheme ( mediaQuery . matches ? 'dark' : 'light' );
mediaQuery . addEventListener ( 'change' , handleChange );
return () => mediaQuery . removeEventListener ( 'change' , handleChange );
}, []);
return theme ;
}
Troubleshooting
Verify that:
The dark class is being toggled on the <html> element
For Tailwind v3, darkMode: 'class' is set in your config
Component styles are imported before Tailwind CSS
Colors not updating on theme change
Check that you’re using the correct CSS variable names in your theme configuration. The components look for specific variables like --color-primary-dark. Also verify that the dark class is actually being applied to the HTML element using browser dev tools.
This happens when the theme is applied after initial render. Use suppressHydrationWarning on the <html> tag and apply the theme class before React hydrates: < script >
if ( localStorage . theme === 'dark' || ( ! ( 'theme' in localStorage ) && window . matchMedia ( '(prefers-color-scheme: dark)' ). matches )) {
document . documentElement . classList . add ( 'dark' );
} else {
document . documentElement . classList . remove ( 'dark' );
}
</ script >
Some components not adapting
Ensure you’re importing the component styles at the top of your CSS file: @import "@mintlify/components/styles.css" ;
The component styles include all necessary dark mode variants.
Next Steps
Tailwind Integration Learn about Tailwind CSS setup and configuration
TypeScript Explore TypeScript types and exports