Package Exports
- nextjs-themes-ultralite
- nextjs-themes-ultralite/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (nextjs-themes-ultralite) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Nextjs-Themes-Ultra
🤟 👉 Unleash the Power of React Server Components
A canonical package with a longer and more descriptive name is also published -
nextjs-themes-ultra
Motivation
I created nextjs-themes
library to achieve functionality like next-themes
with React Server Components. It worked well, however, I noticed issues with tree-shaking and also found that some functions that are rarely used can be removed or replaced to improve overall performance and readability.
I will update the main library nextjs-themes
. However, it requires ensuring minimal changes to the existing APIs. And thus I created a new library, which has the potential to be a better alternative in most cases.
Features
✅ Perfect dark mode in 2 lines of code
✅ Works with Tailwind CSS
✅ Fully Treeshakable (import from @mayank1513/nthul-lite/client/theme-switcher
)
✅ Designed for excellence
✅ Full TypeScript Support
✅ Unleash the full power of React18 Server components
✅ System setting with prefers-color-scheme
✅ Themed browser UI with color-scheme
✅ Support for Next.js 13 & Next.js 14 appDir
✅ No flash on load (for all - SSG, SSR, ISG, Server Components)
✅ Sync theme across tabs and windows - can opt-out by passing dontSync to ThemeSwitcher
✅ Apply custom transition when changing themes
✅ Force pages to specific themes - requires assigning className (detailed techniques comming soon...)
✅ Manipulate theme via useTheme
hook
✅ Documented with Typedoc (Docs)
✅ Use combinations of th-
and dark
or light
classes for dark/light variants of themes
✅ Automatically avoids storing cookies when not using the corresponding ServerTarget
✅ Works with all build systems/tools/frameworks for React18
Feel free to request or discuss new features or report bugs.
Install
A canonical package with a longer and more descriptive name is also published -
nextjs-themes-ultra
$ pnpm add @mayank1513/nthul-lite
or
$ npm install @mayank1513/nthul-lite
or
$ yarn add @mayank1513/nthul-lite
Usage
Please explore
examples
andpackages/shared-ui
for working examples. (updates coming soon...)
SPA (e.g., Vite, CRA) and Next.js pages directory (No server components)
The best way is to add a Custom App
to use by modifying _app
as follows:
Adding dark mode support takes 2 lines of code:
import { ThemeSwitcher, ColorSwitch } from "@mayank1513/nthul-lite/client"; // for better tree-shaking
function MyApp({ Component, pageProps }) {
return (
<>
<ThemeSwitcher />
{/* to add a switch */}
<header>
<ColorSwitch />
</header>
<Component {...pageProps} />
</>
);
}
export default MyApp;
⚡🎉Boom! Just a couple of lines and your dark mode is ready! That too with an awesome color switch for user preferred settings.
Check out examples for advanced usage.
For
vite
or any other build tool you can find a similar root component. E.g.,component in CRA
andvite
.
With Next.js app
router (Server Components)
If your app is mostly serving static content, you do not want the overhead of SSR.
When using this approach, you need to use CSS general sibling Combinator (~) to make sure your themed CSS is properly applied. See (HTML & CSS)[#html--css].
Update your app/layout.jsx
to add ThemeSwitcher
, and ServerTarget
. ServerTarget
is required to avoid a flash of un-themed content on reload.
// app/layout.jsx
import { ThemeSwitcher } from "@mayank1513/nthul-lite/client/theme-switcher"; // for better tree-shaking
import { ServerTarget } from "@mayank1513/nthul-lite/server/nextjs";
export default function Layout({ children }) {
return (
<html lang="en">
<head />
<body>
/** use ServerTarget as first element inside body */
<ServerTarget />
<ThemeSwitcher />
{children}
</body>
</html>
);
}
⚡🎉 Woohoo! You just added multiple theme and color-scheme modes and you can also use Server Component! Isn't that awesome?
ColorSwitch
An elegant color-switch to toggle coloe-schemes ("dark" | "light" | "system").
<ColorSwitch />
HTML & CSS
That's it, your Next.js app fully supports dark mode, including System preference with prefers-color-scheme
. The theme is also immediately synced between tabs. By default, @mayank1513/nthul-lite
modifies the className on the html
element, which you can easily use to style your app:
:root {
/* Your default theme */
--background: white;
--foreground: black;
}
.dark {
--background: black;
--foreground: white;
}
/* for custom themes */
.th-theme1 {
--background: red;
--foreground: yellow;
}
/* for custom theme with dark and light variants */
.dark.th-theme2 {
--background: blue;
--foreground: white;
}
.light.th-theme2 {
--background: white;
--foreground: blue;
}
/* for scoped containers add .nth-scoped call as well - required only when using containerised themes. */
.nth-scoped.th-.dark { /* use th- to make sure outer theme is not applied to container with no theme */
...
}
.nth-scoped.th-theme.dark {
...
}
Also see Example CSS file.
When using ServerTarget
when using ServerTarget
, you need to use CSS general sibling Combinator (~) as ServerTarget
is not wrapping your app.
Replace
.selector
with a combination of selectors from the description above.
/*
for the target element itself, e.g., the html tag.
in most cases, this is inherited by all child elements.
*/
.selector,
/* for forcing to the child elements. */
.selector *,
/* when using ServerTarget */
.selector ~ *, // for all following siblings
.selector ~ * * // for all the children of the following siblings
{
// ...your styles
}
Please note that we have not added a comma (',') after the last selector.
Without comments, it should look like following.
.selector,
.selector *,
.selector ~ *,
.selector ~ * * {
--th-variable: value;
}
We encourage you to use this pattern for defining your theme variables in CSS to avoid any unwanted overrides.
Images
You can also show different images based on the current theme.
import Image from "next/image";
import { useTheme } from "@mayank1513/nthul-lite/hooks";
function ThemedImage() {
const { resolvedColorScheme } = useTheme();
let src;
switch (resolvedColorScheme) {
case "light":
src = "/light.png";
break;
case "dark":
src = "/dark.png";
break;
default:
src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
break;
}
return <Image src={src} width={400} height={400} />;
}
export default ThemedImage;
useTheme
In case your components need to know the current theme and be able to change it. The useTheme
hook provides theme information:
import { useTheme } from "@mayank1513/nthul-lite";
const ThemeChanger = () => {
const { theme, setTheme } = useTheme();
return (
<div>
The current theme is: {theme}
<button onClick={() => setTheme("light")}>Light Mode</button>
<button onClick={() => setTheme("dark")}>Dark Mode</button>
</div>
);
};
useTheme
hook will return following object.
interface UseTheme {
theme: string;
colorSchemePreference: "dark" | "light" | "system";
systemColorScheme: "dark" | "light";
resolvedColorScheme: "dark" | "light";
setColorSchemePreference: (colorSchemePreference: ColorSchemePreference) => void;
setTheme: (theme: string) => void;
}
Force per page theme and color-scheme
We have not added any components or hooks for forcing theme
and color-scheme
per page or per element basis. As this is a rarely used scenario. However, you can acheive this by applying appropreate calssNames.
// force a theme for the page
export default function Page() {
return <div className="dark nth-scoped th-theme1">...</div>;
}
We are open to listening your feedback - Discussions
With Styled Components and any CSS-in-JS
Next Themes is completely CSS independent, it will work with any library. For example, with Styled Components you just need to createGlobalStyle
in your custom App:
// pages/_app.js
import { createGlobalStyle } from "styled-components";
import { ThemeSwitcher } from "@mayank1513/nthul-lite";
// Your themeing variables
const GlobalStyle = createGlobalStyle`
:root {
--fg: #000;
--bg: #fff;
}
[data-theme="dark"] {
--fg: #fff;
--bg: #000;
}
`;
function MyApp({ Component, pageProps }) {
return (
<>
<GlobalStyle />
<ThemeSwitcher />
<Component {...pageProps} />
</>
);
}
With Tailwind
In your tailwind.config.js
, set the dark mode property to class:
// tailwind.config.js
module.exports = {
darkMode: "class",
};
⚡🎉Boom! You are ready to use darkTheme in tailwind.
Caution! Your class must be set to
"dark"
, which is the default value we have used for this library. Tailwind, as of now, requires that class name must be"dark"
for dark-theme.
That's it! Now you can use dark-mode specific classes:
<h1 className="text-black dark:text-white">
Contributing
What's inside?
Utilities
This Turborepo template includes pre-configured tools:
- TypeScript for static type checking
- ESLint for code linting
- Prettier for code formatting
- Plop based code generator for scaffolding new components
- Automatically rebrand this template to match your repo name
Apps and Packages
This Turborepo includes the following packages/examples/lib:
nextjs
: a Next.js appvite
: a Vite.js appfork-me
: a React component library shared by bothNext.js
andVite
exampleseslint-config-custom
: ESLint configurations (includeseslint-config-next
andeslint-config-prettier
)tsconfig
:tsconfig.json
s used throughout the monorepo
Each package/example is 100% TypeScript.
Build
To build all apps and packages, run the following command:
cd @mayank1513/nthul-lite
pnpm build
Develop
To develop all apps and packages, run the following command:
cd @mayank1513/nthul-lite
pnpm dev
🤩 Don't forger to start this repo!
Want hands-on course for getting started with Turborepo? Check out React and Next.js with TypeScript and The Game of Chess with Next.js, React and TypeScrypt
License
Licensed as MIT open source.
with 💖 by Mayank Kumar Chaudhari