Command Palette

Search for a command to run...
HQ UI

Next.Js

Learn how to integrate React Aria in Next.Js

Create Project

Run the init command to create a new Next.js project or to setup an existing one:

npx shadcn@latest init https://hq-ui.vercel.app/r/default

or you can choose any theme listed here

Add components

You can now start adding components to your project.

npx shadcn@latest add https://hq-ui.vercel.app/r/button

Client Providers

To integrate with Next.js (app router), ensure the locale on the server matches the client, and configure React Aria links to use the Next.js router.

components/client-provider.tsx
"use client"

import { useRouter } from 'next/navigation'
import { RouterProvider, I18nProvider } from 'react-aria-components'

// Configure the type of the `routerOptions` prop on all React Aria components.
declare module 'react-aria-components' {
  interface RouterConfig {
    routerOptions: NonNullable<Parameters<ReturnType<typeof useRouter>['push']>[1]>
  }
}

export function ClientProviders({lang, children}) {
  const router = useRouter()

  return (
    <I18nProvider locale={lang}>
      <RouterProvider navigate={router.push}>
        {children}
      </RouterProvider>
    </I18nProvider>
  )
}

Then use in your root layout

app/layout.tsx
import { headers } from 'next/headers'
import { isRTL } from 'react-aria-components'
import { ClientProviders } from '@/components/client-provider'

export default async function RootLayout({children}) {
  // Get the user's preferred language from the Accept-Language header.
  // You could also get this from a database, URL param, etc.
  const acceptLanguage = (await headers()).get('accept-language')
  const lang = acceptLanguage?.split(/[,;]/)[0] || 'en-US'

  return (
    <html lang={lang} dir={isRTL(lang) ? 'rtl' : 'ltr'} suppressHydrationWarning>
      <body>
        <ClientProviders lang={lang}>
          {children}
        </ClientProviders>
      </body>
    </html>
  )
}

Dark Mode

You may use next-themes to implement dark mode in Next.Js

npm i next-themes @tabler/icons-react

Theme Provider

theme-provider.tsx
"use client"

import { ThemeProvider as NextThemesProvider, type ThemeProviderProps, useTheme } from "next-themes"

const ThemeProvider = ({ children, ...props }: ThemeProviderProps) => {
  return (
    <NextThemesProvider enableSystem storageKey="hq-theme" {...props}>
      {children}
    </NextThemesProvider>
  )
}

export { ThemeProvider, useTheme }

Then use in your root layout

app/layout.tsx
import { ClientProviders } from '@/components/client-provider'
import { ThemeProvider } from '@/components/theme-provider'
...
    <html lang={lang} dir={isRTL(lang) ? 'rtl' : 'ltr'} suppressHydrationWarning>
      <body>
        <ClientProviders lang={lang}>
          <ThemeProvider attribute='class'>
            {children}
          </ThemeProvider>
        </ClientProviders>
      </body>
    </html>
...

Theme Switcher

components/theme-switcher.tsx
'use client'

import { useTheme } from '@/components/theme-provider'
import { IconMoon, IconSun } from '@tabler/icons-react'
import { Button } from '@/components/ui/button'

export function ThemeSwitcher({ variant = 'outline' }: { variant?: 'outline' | 'ghost' }) {
  const { resolvedTheme, setTheme } = useTheme()

  return (
    <Button
      aria-label={`Switch to ${resolvedTheme}` === 'light' ? 'dark' : 'light' + 'mode'}
      onPress={() => setTheme(resolvedTheme === 'light' ? 'dark' : 'light')}
      size='icon'
      variant={variant}
    >
      <IconSun aria-hidden className='rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0' />
      <IconMoon aria-hidden className='absolute rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100' />
    </Button>
  )
}