Next.js + TailwindCSS v4: Next-Themes로 다크/라이트 테마 추가하는 방법
Source: Dev.to
TailwindCSS v4는 이제 여러 개의 config 파일을 유지하지 않으며—모든 것이 단일 global.css 파일 안에 들어갑니다. 이로 인해 테마 적용이 다소 까다롭게 느껴질 수 있습니다. 이 가이드에서는 Next.js 프로젝트에서 next‑themes를 사용해 밝은 테마, 어두운 테마, 그리고 사용자 정의 테마까지 손쉽게 설정하는 방법을 보여드리겠습니다.
Step 01 – 프로젝트 시작
👉 Next.js 프로젝트 설치
pnpm create next-app my-project-name
pnpm install
pnpm dev
👉 Next‑Themes 설치
pnpm add next-themes
Source: …
Step 02 – layout.tsx 수정
👉 애플리케이션(자식 컴포넌트)을 <ThemeProvider> 로 감싸기
아래는 기본 Next.js 코드를 유지하면서 프로바이더를 추가한 최소 layout.tsx 예시입니다.
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { ThemeProvider } from "next-themes";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly) {
return (
{children}
);
}
👉 ThemeProvider에 속성 추가하기
{children}
속성 설명
| 속성 | 설명 |
|---|---|
enableSystem={true} | 앱이 사용자의 OS 수준 색상 스키마 설정을 따르도록 합니다. (false가 기본값) |
defaultTheme="system" | 첫 방문 시 어떤 테마가 로드될지 결정합니다. "system"을 사용하면 OS 설정을 그대로 반영합니다. |
⚠️ Hydration 경고 해결
“A tree hydrated but some attributes of the server‑rendered HTML didn’t match the client properties” 라는 경고가 표시되면 <ThemeProvider> 태그에 suppressHydrationWarning 을 추가합니다:
{children}
Note:
ThemeProvider는 클라이언트 컴포넌트이며, 서버 컴포넌트가 아닙니다.
Step 03 – 색상 사용자 정의
👉 global.css
Tailwind import와 다크 모드용 커스텀 변형을 추가합니다:
@import 'tailwindcss';
/* Enable dark variant based on data-theme attribute */
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
Tailwind v4에서는
@import 'tailwindcss'가 필요합니다.
@custom-variant라인은 Tailwind의dark:유틸리티가[data-theme="dark"]속성과 함께 동작하도록 합니다.
사용 예시
Next Theme With TailwindCSS v4
다크 테마가 활성화되면 bg-amber-500가 적용됩니다.
👉 테마 변수 정의
@theme {
/* Light mode background */
--bg-color-light-default: hsl(220, 14%, 96%);
/* Dark mode background */
--bg-color-dark-default: hsl(207, 95%, 8%);
}
👉 변수들을 루트에 적용
:root[data-theme="light"] {
background-color: var(--bg-color-light-default);
}
:root[data-theme="dark"] {
background-color: var(--bg-color-dark-default);
}
이제 전체 애플리케이션이 활성화된 테마에 따라 적절한 배경 색상을 상속받게 됩니다.
Step 04 – 테마 토글러 버튼 추가
우리는 토글 버튼에 lucide‑react 아이콘 라이브러리를 사용할 것입니다.
아이콘 설치
pnpm install lucide-react
ThemeTogglerBtn.tsx 만들기
'use client';
import { useTheme } } from "next-themes";
import { useEffect, useState } from "react";
import { Sun, Moon } from "lucide-react";
export default function ThemeTogglerBtn() {
const [mounted, setMounted] = useState(false);
const { theme, setTheme, resolvedTheme } = useTheme();
// 컴포넌트가 하이드레이션된 후에만 렌더링되도록 보장
useEffect(() => setMounted(true), []);
const toggleTheme = () => {
setTheme(resolvedTheme === "dark" ? "light" : "dark");
};
if (!mounted) {
return (
);
}
const currentIcon =
resolvedTheme === "dark" ? (
) : (
);
return (
{currentIcon}
{resolvedTheme === "dark" ? "Switch to light theme" : "Switch to dark theme"}
);
}
이제 ThemeTogglerBtn을 UI 어디에서든(예: 헤더) 임포트하여 사용자가 라이트 모드와 다크 모드 사이를 전환할 수 있게 할 수 있습니다.
🎉 끝!
이제 다음을 갖추었습니다:
- next‑themes가 설정된 Next.js 프로젝트.
data-theme속성을 인식하도록 구성된 Tailwind v4.- 라이트/다크 배경을 위한 커스텀 CSS 변수.
- 재사용 가능한 클라이언트‑사이드 토글 버튼.
추가적인 커스텀 팔레트나 “시스템‑전용” 모드 등으로 테마 시스템을 확장해 보세요. 즐거운 코딩 되세요!
테마 전환 버튼
{currentIcon}
Theme switcher button
useTheme 훅
import { useTheme } from 'next-themes';
const { theme, setTheme, resolvedTheme } = useTheme();
| 변수 | 설명 |
|---|---|
theme | 현재 선택된 테마('light' 또는 'dark')를 표시합니다. |
setTheme | 테마를 변경할 수 있는 설정 함수입니다. |
resolvedTheme | 시스템 기본 테마를 감지합니다(사용자 기기에서 실제로 활성화된 테마). |
mounted State
When using next-themes in a Next.js app, the theme value can differ between server‑side rendering (SSR) and client‑side hydration. To avoid a mismatch, we track a mounted flag that becomes true only after the component has mounted on the client.
import { useState, useEffect } from 'react';
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
- 초기:
mounted = false(SSR). - 클라이언트 마운트 후:
mounted = true.
This ensures the theme toggler runs only on the client side.
테마 토글러 함수
const toggleTheme = () => {
setTheme(resolvedTheme === 'dark' ? 'light' : 'dark');
};
resolvedTheme이'dark'이면, 함수는'light'로 전환합니다.resolvedTheme이'light'이면,'dark'로 전환합니다.
Icon Selection
import { Sun, Moon } from 'react-feather';
const currentIcon = resolvedTheme === 'dark' ? (
) : (
);
- Dark mode: Sun 아이콘을 렌더링합니다.
- Light mode: Moon 아이콘을 렌더링합니다.
버튼 UI
{currentIcon}
Theme switcher button
- 버튼을 클릭하면
toggleTheme가 실행됩니다. {currentIcon}은(는) 적절한 아이콘을 표시합니다.…은(는) 화면 판독기를 위한 접근성 라벨을 제공합니다.
✅ 한눈에 보기
- 설치
next-themes. - 감싸기 앱을
<ThemeProvider>로. - 설정
global.css를@custom-variant dark(또는 Tailwind의dark:변형)와 함께. - 추가
useTheme훅을 사용하여 테마 토글러 버튼을. - 즐기세요 부드러운 다크/라이트 테마 전환을! 🎉