You can apply css inline as usual:
export default function Home() {
const inlineStyle = {
background: "#000",
padding: ".5rem"
};
return (
<main>
<p style={inlineStyle}>Hello.</p>
</main>
);
}
Global styles can be imported into any layout, page, or component inside the app directory, however it's common to place in your app directory and import it into your root layout:
app
├─about
│ └─page.js
├─contact
│ └─page.js
├─favicon.ico
├─globals.css
├─layout.js
├─page.js
└─page.module.css
app/layout.js
// These styles apply to every route in the application
import './globals.css';
// Root Layout
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
{children}
</body>
</html>
);
}
CSS Modules locally scope CSS by automatically creating a unique class name. This allows you to use the same class name in different files without worrying about collisions.
Files should have a .module.css
extension and can be imported into any file inside the app directory:
import styles from './layout.module.css';
export default function DashboardLayout({ children }) {
return <section className={styles.dashboard}>{children}</section>;
}
Where styles.dashboard
is a class in layout.module.css
:
.dashboard {
padding: 24px;
}
⚠️ with css modules, class names with hyphens will break. The recommendation is to use camelCase instead but you can also use bracket notation instead of dot notation:<div className={styles['nav-wrapper']}>
.
To add multiple styles, use string temlate literals:
<PencilIcon className={`${styles.btn__icon} ${styles['btn__icon--edit']}`} />
Warning: CSS-in-JS libraries which require runtime JavaScript are not currently supported in Server Components.
The procedure for using styled-components
or styled-jsx
is a bit nutty because you have to do all this style registry shit. It seems like they would rather you do css modules.
Install:
npm install styled-components
Then create a client component "style registry" app/_lib/registry.js
:
'use client';
import React, { useState } from 'react';
import { useServerInsertedHTML } from 'next/navigation';
import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
export default function StyledComponentsRegistry({ children }) {
// Only create stylesheet once with lazy initial state
// x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet());
useServerInsertedHTML(() => {
const styles = styledComponentsStyleSheet.getStyleElement();
styledComponentsStyleSheet.instance.clearTag();
return <>{styles}</>;
});
if (typeof window !== 'undefined') return <>{children}</>;
return (
<StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
{children}
</StyleSheetManager>
);
}
Then wrap your app with the style registry:
import StyledComponentsRegistry from './lib/registry.js';
import './globals.css';
// Root Layout
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<StyledComponentsRegistry>
{children}
</StyledComponentsRegistry>
</body>
</html>
);
}
The registry for styled-jsx is a little different.
For me, the main advantage to using styled components is that its easy to implement transitions with react-transition-group.
Install the Tailwind packages and run the init
command to generate both the tailwind.config.js
and postcss.config.js
files:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Inside tailwind.config.js
, add paths to the files that will use Tailwind CSS class names.
You do not need to modify postcss.config.js
.
Add these to your global.css:
@tailwind base;
@tailwind components;
@tailwind utilities;
Note: all the above is done automatically if you choose yes
to tailwind in npx create-next-app@latest
.
Now you can use tailwind:
export default function Home() {
return (
<main>
<p className='text-3xl font-bold underline'>Testing tailwind</p>
</main>
)
}
To use css variables in tailwind, set them as usual in globals.css
:
:root {
--test-color: tomato;
}
[data-theme="dark"] {
--test-color: gold;
}
Then define them in tailwind.config.js
:
module.exports = {
content: [
// ...
],
theme: {
extend: {
colors: {
"test-color": "var(--test-color)",
}
},
},
plugins: [],
}
Then you can use them:
export default function Home() {
return (
<main>
<p className='text-test-color'>Testing tailwind</p>
<p className='bg-test-color'>Testing tailwind</p>
</main>
)
}
clsx is a tiny (239B) utility for constructing className strings conditionally. For example:
import clsx from 'clsx';
import styles from './page.module.css';
export default function Home() {
const status: string = 'error';
return (
<main>
<div
className={`${styles.message} ${status === 'success' ? styles.success : ''}${
status === 'error' ? styles.error : ''
}`}>
<p>string interpolation</p>
</div>
<div
className={clsx(
styles.message,
status === 'success' && styles.success,
status === 'error' && styles.error
)}>
<p>clsx: using comma separated conditions</p>
</div>
<div
className={clsx(styles.message, {
[styles.success]: status === 'success',
[styles.error]: status === 'error'
})}>
<p>clsx: using an object and computed property names</p>
</div>
</main>
);
}
While string interpolation works fine for straightforward cases, clsx shines in scenarios where you have multiple conditional class names or need to combine classes from different sources. It can be more readable, flexible (you can mix strings, objects, and arrays), and less error prone (it omits false values, whereas string interpolation expression like status === 'success' && styles.success
will add the word 'false' to the className if it doesn't reolve to true).
Automatically self-host any Google Font. Fonts are included in the deployment and served from the same domain as your deployment. No requests are sent to Google by the browser.
// Import the font you want to use from next/font/google as a function.
// Next recommends using variable fonts for the performance and flexibility.
// Use an underscore (_) for font names with multiple words.
import { Montserrat, Roboto, Commissioner } from 'next/font/google';
import './globals.css';
// If loading a variable font, you don't need to specify the font weight.
// subsets reduce the size of the font file and improves performance.
// You can find a list of all subsets on the Google Fonts page for your font.
// variable lets you define a css variable name.
const fontMain = Montserrat({ subsets: ['latin'], variable: '--font-main' });
// Some variable fonts have extra axes that can be included. By default,
// only the font weight is included to keep the file size down.
// The possible values of axes depend on the specific font.
const fontAlt = Commissioner({
subsets: ['latin'],
axes: ['slnt'],
variable: '--font-alt' });
// If you can't use a variable font, you will need to specify a weight here.
// You can specify multiple weights and/or styles by using an array.
const roboto = Roboto({
subsets: ['latin'],
weight: ['400', '700'],
style: ['normal', 'italic'],
variable: '--font-roboto'
})
// Root Layout. Pass the variables to the html ot body tags.
export default function RootLayout({ children }) {
return (
<html
lang="en"
className={`${fontMain.variable} ${roboto.variable} ${fontAlt.variable}`}>
<body>
{children}
</body>
</html>
);
}
You can then reference your variable font names anywhere in your css:
body {
font-family: var(--font-main);
}
Variable font axes can be adjusted like so:
.test_variable_font {
font-family: var(--font-alt);
font-size: 3rem;
/* variable font axis: weight */
font-weight: 250;
/* variable font axis: width */
font-stretch: 193%;
/* variable font axis: slant */
font-variation-settings: 'slnt' -5;
/* variable font axis: italic */
font-variation-settings: 'ital' -12;
/* variable font axis: optical size */
font-variation-settings: 'opsz' 48;
/* variable font axis: custom */
font-variation-settings: 'CASL' 0.15;
}
You can also reference you own font files using localFont
:
import type { Metadata } from "next";
import localFont from "next/font/local";
import "./globals.css";
const geistSans = localFont({
src: "./fonts/GeistVF.woff",
variable: "--font-geist-sans",
weight: "100 900",
});
const geistMono = localFont({
src: "./fonts/GeistMonoVF.woff",
variable: "--font-geist-mono",
weight: "100 900",
});
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={`${geistSans.variable} ${geistMono.variable}`}>
{children}
</body>
</html>
);
}
WARNING BUG: For some weird reason I have had troubles when naming my font
main
andmono
:// Fonts const main = Montserrat({ subsets: ['latin'], variable: '--font-main' }); const mono = Noto_Sans_Mono({ subsets: ['latin'], variable: '--font-mono' });In a handful (but not all) of my examples, the font doesn't work when using these names. Only changing the variable names to something else like
fontMain
andfontMono
worked. No idea why.
- See the list of all google variable fonts and their axes
- You can also load local fonts
- See fonts with tailwind
- See the Font API reference