Next.js
Starter Template
Starting fresh? An example application is available in our template repository.
This library requires React 19+
There are four steps to installation:
- Install the npm packages
- Import the CSS
- Configure fonts
- Configure the theme provider
This guide assumes you're using Next.js with the app router.
Install the npm packages
pnpm add @qualcomm-ui/react @qualcomm-ui/react-core @qualcomm-ui/core @qualcomm-ui/qds-core @qualcomm-ui/dom @qualcomm-ui/utils @tanstack/react-virtual lucide-react
npm i @qualcomm-ui/react @qualcomm-ui/react-core @qualcomm-ui/core @qualcomm-ui/qds-core @qualcomm-ui/dom @qualcomm-ui/utils @tanstack/react-virtual lucide-react
yarn add @qualcomm-ui/react @qualcomm-ui/react-core @qualcomm-ui/core @qualcomm-ui/qds-core @qualcomm-ui/dom @qualcomm-ui/utils @tanstack/react-virtual lucide-react
Import the CSS
In your app/globals.css file, import the QUI styles.
@import "@qualcomm-ui/qds-core/styles/components.css";
@import "@qualcomm-ui/qds-core/themes/qualcomm-dark.css";
@import "@qualcomm-ui/qds-core/themes/qualcomm-light.css";
body {
--type-font-family-secondary: var(--font-roboto-flex), sans-serif;
--type-font-family-tertiary: var(--font-roboto-mono), monospace;
}@import "tailwindcss";
/* Recommended: install and import @qualcomm-ui/tailwind-plugin */
@import "@qualcomm-ui/tailwind-plugin/qui-strict.css";
/* Important: always order these imports after the tailwindcss import */
@import "@qualcomm-ui/qds-core/styles/components.css" layer(components);
@import "@qualcomm-ui/qds-core/themes/qualcomm-light.css" layer(components);
@import "@qualcomm-ui/qds-core/themes/qualcomm-dark.css" layer(components);
body {
--type-font-family-secondary: var(--font-roboto-flex);
--type-font-family-tertiary: var(--font-roboto-mono);
}Learn more about the available brands in the theme documentation.
Configure fonts
QUI components are designed with the Roboto Flex and Roboto Mono font families.
In Next.js, configure fonts using next/font/google and set up the theme provider in your root layout:
import type {Metadata} from "next"
import {Roboto_Flex, Roboto_Mono} from "next/font/google"
import "./globals.css"
import {type ReactNode} from "react"
const sansFont = Roboto_Flex({
variable: "--font-roboto-flex",
weight: ["400", "500", "600"],
subsets: ["latin"],
})
const monoFont = Roboto_Mono({
variable: "--font-roboto-mono",
subsets: ["latin"],
})
export const metadata: Metadata = {
title: "Your App Title",
description: "Your app description",
}
export default function RootLayout({
children,
}: Readonly<{
children: ReactNode
}>) {
return (
<html lang="en">
<head>
<link rel="stylesheet" href="https://use.typekit.net/nhs4wvu.css" />
</head>
<body className={`${sansFont.variable} ${monoFont.variable}`}>
{children}
</body>
</html>
)
}Configure the theme provider
QUI styles support both light and dark modes. Choose the approach that matches your rendering strategy:
- SSR: for server-rendered apps. Uses cookies for flash-free theme persistence.
- SSG: for static exports (
output: "export"). UseslocalStoragewith an inline script to prevent theme flash.
SSR
Create the Theme Provider
First, create a ThemeProvider component at app/components/theme-provider.tsx:
"use client"
import {
createContext,
type ReactNode,
useCallback,
useContext,
useState,
} from "react"
import {QdsTheme} from "@qualcomm-ui/qds-core/theme"
interface ThemeContextValue {
setTheme: (theme: QdsTheme) => void
theme: QdsTheme
toggleTheme: () => void
}
const ThemeContext = createContext<ThemeContextValue | null>(null)
export function useTheme(): ThemeContextValue {
const context = useContext(ThemeContext)
if (!context) {
throw new Error("useTheme must be used within a ThemeProvider")
}
return context
}
interface ThemeProviderProps {
children: ReactNode
defaultTheme: QdsTheme
}
export function ThemeProvider({
children,
defaultTheme,
}: ThemeProviderProps): ReactNode {
const [theme, setThemeState] = useState<QdsTheme>(defaultTheme)
const setTheme = useCallback((newTheme: QdsTheme) => {
document.cookie = `app-qds-theme=${newTheme}; path=/; max-age=31536000`
document.documentElement.setAttribute("data-theme", newTheme)
setThemeState(newTheme)
}, [])
const toggleTheme = useCallback(() => {
setTheme(theme === QdsTheme.DARK ? QdsTheme.LIGHT : QdsTheme.DARK)
}, [theme, setTheme])
return (
<ThemeContext.Provider value={{setTheme, theme, toggleTheme}}>
{children}
</ThemeContext.Provider>
)
}Update the root layout
Update your app/layout.tsx to read the theme cookie server-side and pass it to the provider:
import "./globals.css"
import type {ReactNode} from "react"
import type {Metadata} from "next"
import {Roboto_Flex, Roboto_Mono} from "next/font/google"
import {cookies} from "next/headers"
import {QdsTheme} from "@qualcomm-ui/qds-core/theme"
import {ThemeProvider} from "./components/theme-provider"
const sansFont = Roboto_Flex({
subsets: ["latin"],
variable: "--font-roboto-flex",
weight: ["400", "500", "600"],
})
const monoFont = Roboto_Mono({
subsets: ["latin"],
variable: "--font-roboto-mono",
})
export const metadata: Metadata = {
description: "Your app description",
title: "Your App Title",
}
export default async function RootLayout({
children,
}: Readonly<{
children: ReactNode
}>) {
const cookieStore = await cookies()
const themeCookie = cookieStore.get("app-qds-theme")
const theme: QdsTheme =
themeCookie?.value === QdsTheme.LIGHT ? QdsTheme.LIGHT : QdsTheme.DARK
return (
<html lang="en" data-brand="qualcomm" data-theme={theme}>
<head>
<link rel="stylesheet" href="https://use.typekit.net/nhs4wvu.css" />
</head>
<body className={`${sansFont.variable} ${monoFont.variable} antialiased`}>
<ThemeProvider defaultTheme={theme}>{children}</ThemeProvider>
</body>
</html>
)
}Key configuration details:
cookies()reads the theme preference server-side to prevent flash of wrong themedata-theme={theme}on<html>ensures correct theme on first renderdata-brand="qualcomm"sets the brand (change to"snapdragon"or"dragonwing"for other brands)
SSG
For static exports (output: "export"), cookies and server-side APIs are unavailable. This approach uses localStorage for theme persistence and an inline script to apply the saved theme before the page paints.
In your next.config.ts, enable static exports:
import type {NextConfig} from "next"
const nextConfig: NextConfig = {
output: "export",
}
export default nextConfigCreate the Theme Provider
Create a ThemeProvider component at app/components/theme-provider.tsx:
"use client"
import {
createContext,
type ReactNode,
useCallback,
useContext,
useState,
} from "react"
import {QdsTheme} from "@qualcomm-ui/qds-core/theme"
const THEME_STORAGE_KEY = "app-qds-theme"
interface ThemeContextValue {
setTheme: (theme: QdsTheme) => void
theme: QdsTheme
toggleTheme: () => void
}
const ThemeContext = createContext<ThemeContextValue | null>(null)
export function useTheme(): ThemeContextValue {
const context = useContext(ThemeContext)
if (!context) {
throw new Error("useTheme must be used within a ThemeProvider")
}
return context
}
interface ThemeProviderProps {
children: ReactNode
}
export function ThemeProvider({children}: ThemeProviderProps): ReactNode {
const [theme, setThemeState] = useState<QdsTheme>(() => {
if (typeof window === "undefined") {
return QdsTheme.DARK
}
return localStorage.getItem(THEME_STORAGE_KEY) === QdsTheme.LIGHT
? QdsTheme.LIGHT
: QdsTheme.DARK
})
const setTheme = useCallback((newTheme: QdsTheme) => {
localStorage.setItem(THEME_STORAGE_KEY, newTheme)
document.documentElement.setAttribute("data-theme", newTheme)
document.documentElement.style.colorScheme = newTheme
setThemeState(newTheme)
}, [])
const toggleTheme = useCallback(() => {
setTheme(theme === QdsTheme.DARK ? QdsTheme.LIGHT : QdsTheme.DARK)
}, [theme, setTheme])
return (
<ThemeContext.Provider value={{setTheme, theme, toggleTheme}}>
{children}
</ThemeContext.Provider>
)
}Update the root layout
Update your app/layout.tsx. An inline <script> in <head> reads localStorage before paint to prevent a flash of the wrong theme:
import "./globals.css"
import type {ReactNode} from "react"
import type {Metadata} from "next"
import {Roboto_Flex, Roboto_Mono} from "next/font/google"
import {ThemeProvider} from "./components/theme-provider"
const sansFont = Roboto_Flex({
subsets: ["latin"],
variable: "--font-roboto-flex",
weight: ["400", "500", "600"],
})
const monoFont = Roboto_Mono({
subsets: ["latin"],
variable: "--font-roboto-mono",
})
export const metadata: Metadata = {
description: "Your app description",
title: "Your App Title",
}
const themeScript = `
(function() {
var theme = localStorage.getItem("app-qds-theme") || "dark";
document.documentElement.setAttribute("data-theme", theme);
document.documentElement.style.colorScheme = theme;
})();
`
export default function RootLayout({
children,
}: Readonly<{
children: ReactNode
}>) {
return (
<html lang="en" data-brand="qualcomm" suppressHydrationWarning>
<head>
<script dangerouslySetInnerHTML={{__html: themeScript}} />
<link rel="stylesheet" href="https://use.typekit.net/nhs4wvu.css" />
</head>
<body className={`${sansFont.variable} ${monoFont.variable} antialiased`}>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
)
}Key configuration details:
- The inline
<script>runs before paint, readinglocalStorageto setdata-themeandcolorScheme suppressHydrationWarningon<html>is required because the inline script modifies attributes before React hydratesdata-brand="qualcomm"sets the brand (change to"snapdragon"or"dragonwing"for other brands)- The layout is a synchronous function with no
cookies()import
Your application is now ready to import and use our components.
Next Steps
- Learn more about theming in our theming documentation.
- Use the navigation menu on the left to view component documentation and examples.
Using the theme
Import the useTheme hook from your theme provider to access and toggle the theme:
import {useTheme} from "./components/theme-provider"
function ThemeToggle() {
const {theme, toggleTheme} = useTheme()
return <button onClick={toggleTheme}>Current theme: {theme}</button>
}Customize the default theme
The default theme is "dark". To change the default to "light", modify the fallback depending on your approach.
SSR
update the fallback in your layout:
const theme: QdsTheme = themeCookie?.value === "dark" ? "dark" : "light"SSG
Update 3 places:
- The
useStateinitializer intheme-provider.tsx:
return localStorage.getItem(THEME_STORAGE_KEY) === "dark" ? "dark" : "light"- The server-side fallback in the same file:
if (typeof window === "undefined") return "light"- The inline script in
layout.tsx:
var theme = localStorage.getItem("app-qds-theme") || "light"To set a different brand, change the data-brand attribute on the <html> element:
<html lang="en" data-brand="snapdragon" data-theme={theme} style={{colorScheme: theme}}>Troubleshooting
Styles aren't loading
If @qualcomm-ui/react components are appearing without styles, check the following:
- Ensure that you've imported the CSS from step 2.
- Verify that you've configured the theme provider from step 4.
If styles still aren't appearing, inspect the DOM of your application in your browser's dev tools. Your application's <html> element should have at least two attributes:
data-brand: the QDS brand (eitherqualcomm,snapdragon, ordragonwing)data-theme: the QDS theme (eitherlightordark)
Your <html> element should look something like this:
<html
data-brand="qualcomm"
data-theme="light"
style="color-scheme: light"
></html>Note that you need to import the corresponding CSS for the brand you've selected:
@import "@qualcomm-ui/qds-core/themes/qualcomm-dark.css";
@import "@qualcomm-ui/qds-core/themes/qualcomm-light.css";@import "@qualcomm-ui/qds-core/themes/snapdragon-dark.css";
@import "@qualcomm-ui/qds-core/themes/snapdragon-light.css";@import "@qualcomm-ui/qds-core/themes/dragonwing-dark.css";
@import "@qualcomm-ui/qds-core/themes/dragonwing-light.css";Component Styles
Always import component styles, regardless of the chosen brand.
@import "@qualcomm-ui/qds-core/styles/components.css";Migrating from legacy QUI
NextGen is independent of the legacy @qui/react package. Both can be installed and used in the same project without conflicts.
/* this is fine */
@import "@qui/styles/dist/all.min.css";
@import "@qui/base/dist/all.min.css";
@import "@qualcomm-ui/qds-core/styles/components.css";
@import "@qualcomm-ui/qds-core/themes/qualcomm-dark.css";
@import "@qualcomm-ui/qds-core/themes/qualcomm-light.css";