Dark Mode for React
NextStepjs doesn't enforce any specific dark mode implementation, giving you complete control over how dark mode works in your React application.
How Dark Mode Works
Dark mode in NextStepjs is entirely controlled by your application's theme system. If you're using custom card components and have placed your NextStep component inside a theme provider, dark mode will work seamlessly.
Important:
NextStepjs doesn't have its own dark mode toggle or detection. It inherits styling from your application's theme context.
Using with Theme Providers
To ensure your NextStepjs tours support dark mode, simply wrap your application (including the NextStep component) with your theme provider:
import { NextStepReact, NextStepProvider } from 'nextstepjs';
import { ThemeProvider } from 'your-theme-provider';
function MyApp({ Component, pageProps }) {
return (
<ThemeProvider>
<NextStepProvider>
<NextStepReact steps={steps}>
<Component {...pageProps} />
</NextStepReact>
</NextStepProvider>
</ThemeProvider>
);
}
Custom Cards with Dark Mode Support
When creating custom card components, you can add dark mode support by using your application's theme context or CSS variables:
'use client';
import React from 'react';
import { Step } from 'nextstepjs';
import { useTheme } from 'your-theme-hook'; // Your theme hook
interface DarkModeCardProps {
step: Step;
currentStep: number;
totalSteps: number;
nextStep: () => void;
prevStep: () => void;
skipTour: () => void;
arrow: React.ReactNode;
}
const DarkModeCard = ({
step,
currentStep,
totalSteps,
nextStep,
prevStep,
skipTour,
arrow,
}: DarkModeCardProps) => {
const { isDarkMode } = useTheme(); // Get theme from your context
return (
<div
className={`${
isDarkMode ? 'bg-gray-800 text-white' : 'bg-white text-gray-800'
} rounded-lg shadow-lg p-6 max-w-md`}
>
<div className="flex items-center gap-3 mb-4">
{step.icon && <div className="text-2xl">{step.icon}</div>}
<h3 className="text-xl font-bold">{step.title}</h3>
</div>
<div className="mb-6">{step.content}</div>
{arrow}
<div className="flex justify-between items-center">
<div className="text-sm">
Step {currentStep + 1} of {totalSteps}
</div>
<div className="flex gap-2">
{currentStep > 0 && (
<button
onClick={prevStep}
className={`px-4 py-2 ${
isDarkMode ? 'bg-gray-700 text-gray-300' : 'bg-gray-200 text-gray-700'
} rounded`}
>
Previous
</button>
)}
<button
onClick={nextStep}
className={`px-4 py-2 ${
isDarkMode ? 'bg-blue-600' : 'bg-blue-500'
} text-white rounded`}
>
{currentStep === totalSteps - 1 ? 'Finish' : 'Next'}
</button>
{step.showSkip && (
<button
onClick={skipTour}
className={`px-4 py-2 ${
isDarkMode ? 'text-gray-400' : 'text-gray-500'
}`}
>
Skip
</button>
)}
</div>
</div>
</div>
);
};
export default DarkModeCard;
Example with Tailwind CSS
If you're using Tailwind CSS with the dark mode class strategy, your custom card can use Tailwind's dark variant:
'use client';
import React from 'react';
import { Step } from 'nextstepjs';
interface TailwindDarkModeCardProps {
step: Step;
currentStep: number;
totalSteps: number;
nextStep: () => void;
prevStep: () => void;
skipTour: () => void;
arrow: React.ReactNode;
}
const TailwindDarkModeCard = ({
step,
currentStep,
totalSteps,
nextStep,
prevStep,
skipTour,
arrow,
}: TailwindDarkModeCardProps) => {
return (
<div className="bg-white dark:bg-gray-800 text-gray-800 dark:text-white rounded-lg shadow-lg p-6 max-w-md">
<div className="flex items-center gap-3 mb-4">
{step.icon && <div className="text-2xl">{step.icon}</div>}
<h3 className="text-xl font-bold">{step.title}</h3>
</div>
<div className="mb-6">{step.content}</div>
{arrow}
<div className="flex justify-between items-center">
<div className="text-sm">
Step {currentStep + 1} of {totalSteps}
</div>
<div className="flex gap-2">
{currentStep > 0 && (
<button
onClick={prevStep}
className="px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded"
>
Previous
</button>
)}
<button
onClick={nextStep}
className="px-4 py-2 bg-blue-500 dark:bg-blue-600 text-white rounded"
>
{currentStep === totalSteps - 1 ? 'Finish' : 'Next'}
</button>
{step.showSkip && (
<button
onClick={skipTour}
className="px-4 py-2 text-gray-500 dark:text-gray-400"
>
Skip
</button>
)}
</div>
</div>
</div>
);
};
export default TailwindDarkModeCard;
Live Example
Here's a simple example of a card that changes based on dark mode. Toggle the switch below to see how it adapts:
Light Mode Card
With Shadcn UI
If you're using Shadcn UI (which uses CSS variables for theming), your custom card will automatically adapt to dark mode when your application's theme changes:
'use client';
import React from 'react';
import { Step } from 'nextstepjs';
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
interface ShadcnDarkModeCardProps {
step: Step;
currentStep: number;
totalSteps: number;
nextStep: () => void;
prevStep: () => void;
skipTour: () => void;
arrow: React.ReactNode;
}
const ShadcnDarkModeCard = ({
step,
currentStep,
totalSteps,
nextStep,
prevStep,
skipTour,
arrow,
}: ShadcnDarkModeCardProps) => {
return (
<Card className="w-[350px]">
<CardHeader>
<CardTitle className="flex items-center gap-2">
{step.icon && <span>{step.icon}</span>}
{step.title}
</CardTitle>
</CardHeader>
<CardContent>
<div className="mb-2">{step.content}</div>
{arrow}
</CardContent>
<CardFooter className="flex justify-between">
<div className="text-sm text-muted-foreground">
{currentStep + 1} / {totalSteps}
</div>
<div className="flex gap-2">
{currentStep > 0 && (
<Button variant="outline" size="sm" onClick={prevStep}>
Previous
</Button>
)}
<Button size="sm" onClick={nextStep}>
{currentStep === totalSteps - 1 ? 'Finish' : 'Next'}
</Button>
{step.showSkip && (
<Button variant="ghost" size="sm" onClick={skipTour}>
Skip
</Button>
)}
</div>
</CardFooter>
</Card>
);
};
export default ShadcnDarkModeCard;
Overlay in Dark Mode
When using dark mode, you might want to adjust the overlay color to better match your dark theme. You can do this using the shadowRgb
prop:
import { useTheme } from 'your-theme-hook';
function MyApp({ Component, pageProps }) {
const { isDarkMode } = useTheme();
return (
<NextStepProvider>
<NextStep
steps={steps}
shadowRgb={isDarkMode ? "255, 255, 255" : "0, 0, 0"}
shadowOpacity={isDarkMode ? "0.15" : "0.2"}
>
<Component {...pageProps} />
</NextStep>
</NextStepProvider>
);
}
In dark mode, you might want to use a lighter shadow color with lower opacity for a more subtle effect.