Remix Integration
Set up react-tourlight with Remix, including client-only rendering considerations.
react-tourlight uses browser APIs and must run on the client. Remix supports this through its ClientOnly wrapper pattern and *.client.tsx file convention.
Basic setup
1. Create the provider wrapper
import { SpotlightProvider } from 'react-tourlight'
import 'react-tourlight/styles.css'
export function SpotlightWrapper({ children }: { children: React.ReactNode }) {
return (
<SpotlightProvider theme="auto">
{children}
</SpotlightProvider>
)
}2. Add to your root route
Wrap your app in the provider inside app/root.tsx:
import { Outlet } from '@remix-run/react'
import { SpotlightWrapper } from '~/components/spotlight-wrapper'
export default function App() {
return (
<html lang="en">
<head>{/* ... */}</head>
<body>
<SpotlightWrapper>
<Outlet />
</SpotlightWrapper>
</body>
</html>
)
}3. Define tours in route components
import { SpotlightTour, useSpotlight } from 'react-tourlight'
export default function Dashboard() {
const { start } = useSpotlight()
return (
<div>
<SpotlightTour
id="dashboard-tour"
steps={[
{
target: '#stats-panel',
title: 'Statistics',
content: 'Your key metrics at a glance.',
placement: 'bottom',
},
{
target: '#recent-activity',
title: 'Activity',
content: 'See what happened recently.',
placement: 'right',
},
]}
/>
<button onClick={() => start('dashboard-tour')}>
Start Tour
</button>
<div id="stats-panel">{/* ... */}</div>
<div id="recent-activity">{/* ... */}</div>
</div>
)
}Client-only rendering
If you encounter hydration issues (unlikely, since react-tourlight renders no server markup), you can use Remix's ClientOnly utility:
import { ClientOnly } from 'remix-utils/client-only'
import { SpotlightTour } from 'react-tourlight'
import type { SpotlightStep } from 'react-tourlight'
const steps: SpotlightStep[] = [
{
target: '#feature',
title: 'New Feature',
content: 'Check this out.',
placement: 'bottom',
},
]
export function ClientOnlyTour() {
return (
<ClientOnly fallback={null}>
{() => (
<SpotlightTour
id="feature-tour"
steps={steps}
/>
)}
</ClientOnly>
)
}Alternatively, use the .client.tsx file suffix to ensure the module is only loaded on the client:
import { SpotlightTour } from 'react-tourlight'
export function Tour() {
return (
<SpotlightTour
id="onboarding"
steps={[
{ target: '#search', title: 'Search', content: 'Find anything.' },
]}
/>
)
}Multi-page tours with Remix
Use useLocation from @remix-run/react for route-aware steps:
import { useLocation } from '@remix-run/react'
import { SpotlightTour } from 'react-tourlight'
export function OnboardingTour() {
const location = useLocation()
return (
<SpotlightTour
id="onboarding"
steps={[
{
target: '#dashboard-header',
title: 'Dashboard',
content: 'Your home base.',
when: () => location.pathname === '/dashboard',
},
{
target: '#settings-form',
title: 'Settings',
content: 'Configure your preferences.',
when: () => location.pathname === '/settings',
},
]}
/>
)
}See the Multi-Page Tours guide for a full persistence setup.