NextStep - Lightweight Next.js Onboarding Library Logo

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

This card automatically adapts to light mode based on the current theme setting.
Step 1 of 3

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.