<Switch />
+
+const Switch = ({ tag: Tag = "button", size = 24, skipSystem, ...props }: SwitchProps) => {
const [state, setState] = useStore();
const handleModeSwitch = () => {
let index = modes.indexOf(state.m);
if (skipSystem && index === modes.length - 1) index = 0;
setState({
...state,
m: modes[(index + 1) % modes.length],
});
};
const className = [props.className, styles["switch"]].filter(Boolean).join(" ");
return (
<Tag
{...props}
className={className}
// @ts-expect-error -> we are setting the CSS variable
style={{ "--size": `${size}px` }}
data-testid="switch"
onClick={handleModeSwitch}
/>
);
}
+
+const [] = useMode(options);
+
+const useMode = (): UseModeYeild => {
const [{ m: mode, s: systemMode }, setState] = useStore();
const setMode = (m: ColorSchemePreference) => {
setState(prev => ({ ...prev, m }));
};
return {
mode,
systemMode,
resolvedMode: (mode === SYSTEM ? systemMode : mode) as ResolvedScheme, // resolvedMode is the actual mode that will be used
setMode,
};
}
+
+Nextjs Darkmode is a versatile library crafted to fully utilize React 18 server components, ensuring a seamless dark mode experience in Next.js applications. Lightweight and efficient, it respects both user preferences and system settings through the prefers-color-scheme media query, and integrates effortlessly with React/Vite, Remix, and Next.js.
+The nextjs-themes
library was initially created to achieve a similar functionality to next-themes
with React Server Components. While effective, it felt bulky for those supporting only dark/light mode. Thus, nextjs-darkmode
was developed to offer a minimal footprint while utilizing Next.js Server Components, avoiding any flash of unthemed content, and ensuring theme synchronization with the server.
✅ Simple API to toggle between dark and light modes
+✅ Perfect dark mode with just 2 lines of code
+✅ Compatible with Tailwind CSS
+✅ Fully treeshakable (e.g., import from nextjs-darkmode/hooks
)
✅ Full TypeScript support
+✅ Utilizes React 18 Server components
+✅ Compatible with all React 18 build systems/tools/frameworks
+✅ System setting with prefers-color-scheme
✅ Supports Next.js 13 & 14 appDir
✅ No flash on load (supports SSG, SSR, ISG, and Server Components)
+✅ Sync theme across tabs and windows
+✅ Apply custom transitions when changing themes
+✅ Manipulate theme via the useMode
hook
✅ No cookies when not using the corresponding ServerTarget
✅ Comprehensive documentation with Typedoc
+Feel free to request new features, discuss, or report bugs.
+Please consider starring this repository and sharing it with your friends.
+$ pnpm add nextjs-darkmode
+
+or
+$ npm install nextjs-darkmode
+
+or
+$ yarn add nextjs-darkmode
+
+Import styles globally or within layout component.
+/* globals.css */
@import "nextjs-darkmode/css";
+
+// layout.tsx
import "nextjs-darkmode/css";
+
+For a lighter version, use nextjs-darkmode-lite
:
$ pnpm add nextjs-darkmode-lite
+
+or
+$ npm install nextjs-darkmode-lite
+
+or
+$ yarn add nextjs-darkmode-lite
+
+++You need
+r18gs
as a peer-dependency.
++Please explore
+examples
andpackages/shared-ui
for more working examples. (updates coming soon...)
Modify _app
to add dark mode support:
import { Core } from "nextjs-darkmode"; // for better tree-shaking
import { Switch } from "nextjs-darkmode/switch";
function MyApp({ Component, pageProps }) {
return (
<>
<Core />
<header>
<Switch />
</header>
<Component {...pageProps} />
</>
);
}
export default MyApp;
+
+⚡🎉Boom! Just a couple of lines and your dark mode is ready, complete with a color switch for user preferences. Check out examples for advanced usage.
+++For
+vite
or any other build tool, find a similar root component, e.g.,<App />
inCRA
andvite
.
app
router (Server Components)Update app/layout.jsx
to add Core
and ServerTarget
to avoid flash of un-themed content:
// app/layout.jsx
import { Core } from "nextjs-darkmode"; // for better tree-shaking
import { ServerTarget } from "nextjs-darkmode/server";
export default function Layout({ children }) {
return (
<html lang="en">
<head />
<body>
<ServerTarget />
<Core />
{children}
</body>
</html>
);
}
+
+An elegant color switch to toggle color schemes:
+<Switch />
+
+Fully support dark mode, including system preference with prefers-color-scheme
. The dark/light mode is synced between tabs and modifies the className
and data-attributes on the html
or ServerTarget element:
:root {
--background: white;
--foreground: black;
}
.dark {
--background: black;
--foreground: white;
}
/* or */
[data-rm="dark"] {...}
When using `ServerTarget`, use the CSS general sibling combinator (~):
```css
.selector,
.selector *,
.selector ~ *,
.selector ~ * * {
--th-variable: value;
}
```
#### data attributes
`data-rm` -> Resolved Mode
`data-m` -> User's preference
`data-sm` -> System preference
### Images
Show different images based on the current theme:
```jsx
import Image from "next/image";
import { useMode } from "nextjs-darkmode/hooks";
function ThemedImage() {
const { resolvedMode } = useMode();
let src;
switch (resolvedMode) {
case "light":
src = "/light-mode-image.png";
break;
case "dark":
src = "/dark-mode-image.png";
break;
default:
src = "/default-image.png";
break;
}
return <Image src={src} alt="Themed Image" />;
}
```
### useMode
The `useMode` hook provides mode information:
```js
import { useMode } from "nextjs-darkmode";
const ThemeChanger = () => {
const { resolvedMode, setMode } = useMode();
return (
<div>
The current resolved mode is: {resolvedMode}
<button onClick={() => setMode("light")}>Light Mode</button>
<button onClick={() => setMode("dark")}>Dark Mode</button>
</div>
);
};
```
`useMode` hook returns the following object:
```ts
export interface UseModeInterface {
mode: ColorSchemePreference;
systemMode: ResolvedScheme;
resolvedMode: ResolvedScheme;
setMode: (mode: ColorSchemePreference) => void;
}
```
### Force per page mode
Apply appropriate class names and data attributes to force a mode for the page:
```tsx
export default function Page() {
return <div className="dark ndm-scoped data-rm='dark'">...</div>;
}
```
### With Styled Components and any CSS-in-JS
Next Themes works with any library. For example, with Styled Components:
```js
// pages/_app.js
import { createGlobalStyle } from "styled-components";
import { Core } from "nextjs-darkmode";
// Your theming variables
const GlobalStyle = createGlobalStyle`
:root {
--fg: #000;
--bg: #fff;
}
[data-rm="dark"] {
--fg: #fff;
--bg: #000;
}
`;
function MyApp({ Component, pageProps }) {
return (
<>
<GlobalStyle />
<Core />
<Component {...pageProps} />
</>
);
}
```
### With Tailwind
In `tailwind.config.js`, set the dark mode property to class:
```js
// tailwind.config.js
module.exports = {
darkMode: "class",
};
```
Now you can use dark-mode specific classes:
```tsx
<h1 className="text-black dark:text-white">
```
## Performance
`nextjs-darkmode` is designed to be fully tree-shakable, including only the code you use. For instance, if you only use the `useMode` hook, the rest of the library's code will be removed during the build process.
## Contributing
We welcome contributions! Check out the [Contributing Guide](https://github.com/react18-tools/nextjs-darkmode/blob/main/CONTRIBUTING.md) for more details.
### 🤩 Don't forget to star [this repo](https://github.com/react18-tools/nextjs-darkmode)!
Explore hands-on courses to get started with Turborepo:
- [React and Next.js with TypeScript](https://mayank-chaudhari.vercel.app/courses/react-and-next-js-with-typescript)
- [The Game of Chess with Next.js, React, and TypeScript](https://www.udemy.com/course/game-of-chess-with-nextjs-react-and-typescrypt/?referralCode=851A28F10B254A8523FE)
![Repo Stats](https://repobeats.axiom.co/api/embed/85eec5cd9a0ede65ac366f834ada1a170ef775c8.svg "Repobeats analytics image")
## License
[MPL-2.0](https://github.com/react18-tools/nextjs-darkmode/blob/main/LICENSE)
Feel free to use, modify, and distribute this library under the MPL-2.0 license.
Please consider enrolling in [our courses](https://mayank-chaudhari.vercel.app/courses) or [sponsoring](https://github.com/sponsors/mayank1513) our work.
<hr />
<p align="center" style="text-align:center">with 💖 by <a href="https://mayank-chaudhari.vercel.app" target="_blank">Mayank Kumar Chaudhari</a></p>
+
+Optional
sizeDiameter of the color switch
+Optional
skipSkip system colorScheme while toggling
+Optional
taghtml tag
+'button'
+
+Const
Const
Const
Const
Const
Const
Example
+Source
+- Source code
+
+ +