Links
🔧

Initial setup

Setup @codegouvfr/react-dsfr in your project
If you already had the DSFR installed in your project, let's start from scratch:
  • Remove @gouvfr/dsfr from your dependencies.
  • Remove the import ofdsfr.css, dsfr.module.js the favicon and the fonts.
  • Remove the data-fr-scheme (and data-fr-theme ) attribude from your <html/> tag
yarn
npm
pnpm
Yarn Berry (a.k.a Yarn 3 or Yarn modern)
yarn add @codegouvfr/react-dsfr
npm install --save @codegouvfr/react-dsfr
pnpm add @codegouvfr/react-dsfr
And add this file to the root of your project, to enable pre & post scripts with pnpm:
.npmrc
enable-pre-post-scripts=true
When we say Yarn we usually refer to Yarn 1.x as most dev teams (Including me) havent upgraded to the newest version (for good reasons).
If you want to use Yarn Berry you be aware that pre- post- scripts aren't supported.
So you must do something like "dev": "copy-dsfr-to-public && next dev" (same thing for start)
Also you must configure it so it uses node_modules (sorry)
Vite
Next.js App Router
Next.js Page Router
Create React App
Other
Add theses three scipts to your package.json:
package.json
"scripts": {
"postinstall": "copy-dsfr-to-public",
"predev": "only-include-used-icons",
"prebuild": "only-include-used-icons"
}
Trigger the execution of the postinstall script by running:
yarn # Or 'npm install' or 'pnpm install'
Add the following tags in the <head />
index.html
<link rel="apple-touch-icon" href="/dsfr/favicon/apple-touch-icon.png" />
<link rel="icon" href="/dsfr/favicon/favicon.svg" type="image/svg+xml" />
<link rel="shortcut icon" href="/dsfr/favicon/favicon.ico" type="image/x-icon" />
<link rel="manifest" href="/dsfr/favicon/manifest.webmanifest" crossorigin="use-credentials" />
<link rel="stylesheet" href="/dsfr/utility/icons/icons.min.css" />
<link rel="stylesheet" href="/dsfr/dsfr.min.css" />
src/main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import { App } from "./App";
import { startReactDsfr } from "@codegouvfr/react-dsfr/spa";
startReactDsfr({ defaultColorScheme: "system" });
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
You're all set! Next step for you is to setup de integration with your routing library (react-router for example)
This documentation is for Next projects using the App router.
You are in this case if you have a app/ directory at the root of your project.
I understand that the setup process for Next AppRouter can be quite complex, but please rest assured that I have made every effort to simplify it as much as possible.
The complexity arises largely due to limitations within the current Next.js App Router. For instance, it does not support higher-order functions, lacks a reliable sequence for applying global stylesheets, and doesn't clearly delineate a way to run code solely on the client-side, among other deficiencies.
Despite the setup process not being as streamlined as one might hope, I can confidently say that react-dsfr's integration with Next App Router is as comprehensive and effective as it can be under current circumstances (see demo). Most component featured in this toolkit are RSC ready, thoses that are not are labeled using the "use client" directive.
yarn add --dev sass
#OR
npm install --save-dev sass
next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true, // Recommended for the `pages` directory, default in `app`.
swcMinify: true,
experimental: {
// Required:
appDir: true,
},
webpack: config => {
config.module.rules.push({
test: /\.woff2$/,
type: "asset/resource"
});
return config;
}
};
module.exports = nextConfig;
package.json
"scripts": {
"predev": "only-include-used-icons",
"prebuild": "only-include-used-icons"
}
app/defaultColorScheme.ts
import type { DefaultColorScheme } from "@codegouvfr/react-dsfr/next-appdir";
export const defaultColorScheme: DefaultColorScheme = "system";
app/StartDsfr.tsx
"use client";
import { startReactDsfr } from "@codegouvfr/react-dsfr/next-appdir";
import { defaultColorScheme } from "./defaultColorScheme";
import Link from "next/link";
declare module "@codegouvfr/react-dsfr/next-appdir" {
interface RegisterLink {
Link: typeof Link;
}
}
startReactDsfr({ defaultColorScheme, Link });
export function StartDsfr(){
//Yes, leave null here.
return null;
}
app/layout.tsx
import { DsfrHead } from "@codegouvfr/react-dsfr/next-appdir/DsfrHead";
import { DsfrProvider } from "@codegouvfr/react-dsfr/next-appdir/DsfrProvider";
import { getHtmlAttributes } from "@codegouvfr/react-dsfr/next-appdir/getHtmlAttributes";
import { StartDsfr } from "./StartDsfr";
import { defaultColorScheme } from "./defaultColorScheme";
import Link from "next/link";
export default function RootLayout({ children }: { children: JSX.Element; }) {
//NOTE: The lang parameter is optional and defaults to "fr"
const lang = "fr";
return (
<html {...getColorSchemeHtmlAttributes({ defaultColorScheme, lang })} >
<head>
<StartDsfr />
<DsfrHead
defaultColorScheme={defaultColorScheme}
Link={Link}
/>
</head>
<body>
<DsfrProvider lang={lang}>
{children}
</DsfrProvider>
</body>
</html>
);
}
Yes MUI is supported in AppDir thanks to TSS. See instructions.
You may experience white flashes in dev mode but not in production. 👍
This documentation is for Next projects using the Page Router (aka the legacy next setup).
You are in this case if you have a pages/ directory at the root of your project.
next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
webpack: config => {
config.module.rules.push({
test: /\.woff2$/,
type: "asset/resource"
});
return config;
},
//This option requires Next 13.1 or newer, if you can't update you can use this plugin instead: https://github.com/martpie/next-transpile-modules
transpilePackages: ["@codegouvfr/react-dsfr", "tss-react"]
};
module.exports = nextConfig
package.json
"scripts": {
"predev": "only-include-used-icons",
"prebuild": "only-include-used-icons"
}
pages/_app.tsx
import type { AppProps } from "next/app";
import { createNextDsfrIntegrationApi } from "@codegouvfr/react-dsfr/next-pagesdir";
import Link from "next/link";
// Only in TypeScript projects
declare module "@codegouvfr/react-dsfr/next-pagesdir" {
interface RegisterLink {
Link: typeof Link;
}
}
const {
withDsfr,
dsfrDocumentApi
} = createNextDsfrIntegrationApi({
defaultColorScheme: "system",
Link
});
export { dsfrDocumentApi };
function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}
export default withDsfr(App);
pages/_document.tsx
import { Html, Head, Main, NextScript, DocumentProps } from "next/document";
import { dsfrDocumentApi } from "./_app";
const {
getColorSchemeHtmlAttributes,
augmentDocumentForDsfr
} = dsfrDocumentApi;
export default function Document(props: DocumentProps) {
return (
<Html {...getColorSchemeHtmlAttributes(props)}>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
augmentDocumentForDsfr(Document);
The create-react-app project is no longer being maintained. If you are starting a new project you'll probably be beter off with Vite.
Add theses three scipts to your package.json:
package.json
"scripts": {
...
"postinstall": "copy-dsfr-to-public"
"prestart": "only-include-used-icons",
"prebuild": "only-include-used-icons"
},
...
"jest": {
"transformIgnorePatterns": [
"node_modules/([email protected]/react-dsfr)"
]
}
Trigger the execution of the postinstall script by running:
yarn # Or 'npm install' or 'pnpm install'
Add the following code in the <head />
public/index.html
<link rel="apple-touch-icon" href="%PUBLIC_URL%/dsfr/favicon/apple-touch-icon.png" />
<link rel="icon" href="%PUBLIC_URL%/dsfr/favicon/favicon.svg" type="image/svg+xml" />
<link rel="shortcut icon" href="%PUBLIC_URL%/dsfr/favicon/favicon.ico" type="image/x-icon" />
<link rel="manifest" href="%PUBLIC_URL%/dsfr/favicon/manifest.webmanifest" crossorigin="use-credentials" />
<link rel="stylesheet" href="%PUBLIC_URL%/dsfr/utility/icons/icons.min.css" />
<link rel="stylesheet" href="%PUBLIC_URL%/dsfr/dsfr.min.css" />
src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { startReactDsfr } from "@codegouvfr/react-dsfr/spa";
startReactDsfr({ defaultColorScheme: "system" });
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
You're all set! Next step for you is to setup de integration with your routing library (react-router for example)
Your framwork isn't supported? let's get in touch!

Avoiding or flash of unstyled text (FOUT)

You can avoid having a flash of unstiled text by preloading the font variant used on your homepage.(look in the network tab of your browser dev tool what are the font downloaded initially).
Create React App
Next.js
Next.js AppDir
Vite
Add the following code in the <head />
public/index.html
<%
[
//"Marianne-Light",
//"Marianne-Light_Italic",
"Marianne-Regular",
//"Marianne-Regular_Italic",
"Marianne-Medium",
//"Marianne-Medium_Italic",
"Marianne-Bold",
//"Marianne-Bold_Italic",
//"Spectral-Regular",
//"Spectral-ExtraBold"
].forEach(function(name){ %>
<link rel="preload" href="%PUBLIC_URL%/dsfr/fonts/<%=name%>.woff2" as="font" crossorigin="anonymous" />
<% }); %>
pages/_app.tsx
import type { AppProps } from "next/app";
import { createNextDsfrIntegrationApi } from "@codegouvfr/react-dsfr/next-pagesdir";
import Link from "next/link";
// Only in TypeScript projects
declare module "@codegouvfr/react-dsfr/next-pagesdir" {
interface RegisterLink {
Link: typeof Link;
}
}
const {
withDsfr,
dsfrDocumentApi
} = createNextDsfrIntegrationApi({
defaultColorScheme: "system",
Link,
preloadFonts: [
//"Marianne-Light",
//"Marianne-Light_Italic",
"Marianne-Regular",
//"Marianne-Regular_Italic",
"Marianne-Medium",
//"Marianne-Medium_Italic",
"Marianne-Bold",
//"Marianne-Bold_Italic",
//"Spectral-Regular",
//"Spectral-ExtraBold"
]
});
export { dsfrDocumentApi };
function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}
export default withDsfr(App);
app/layout.tsx
import { DsfrHead } from "@codegouvfr/react-dsfr/next-appdir/DsfrHead";
import { DsfrProvider } from "@codegouvfr/react-dsfr/next-appdir/DsfrProvider";
import { getColorSchemeHtmlAttributes } from "@codegouvfr/react-dsfr/next-appdir/getColorSchemeHtmlAttributes";
import StartDsfr from "./StartDsfr";
import { defaultColorScheme } from "./defaultColorScheme";
export default function RootLayout({ children }: { children: JSX.Element; }) {
return (
<html {...getColorSchemeHtmlAttributes({ defaultColorScheme })} >
<head>
<StartDsfr />
<DsfrHead
defaultColorScheme={defaultColorScheme}
preloadFonts={[
//"Marianne-Light",
//"Marianne-Light_Italic",
"Marianne-Regular",
//"Marianne-Regular_Italic",
"Marianne-Medium",
//"Marianne-Medium_Italic",
"Marianne-Bold"
//"Marianne-Bold_Italic",
//"Spectral-Regular",
//"Spectral-ExtraBold"
]}
/>
</head>
<body>
<DsfrProvider defaultColorScheme={defaultColorScheme}>
{children}
</DsfrProvider>
</body>
</html>
);
}
Add the following tags in the <head />
index.html
<!--<link rel="preload" href="/dsfr/fonts/Marianne-Light.woff2" as="font" crossorigin="anonymous" />-->
<!--<link rel="preload" href="/dsfr/fonts/Marianne-Light_Italic.woff2" as="font" crossorigin="anonymous" />-->
<link rel="preload" href="/dsfr/fonts/Marianne-Regular.woff2" as="font" crossorigin="anonymous" />
<!--<link rel="preload" href="/dsfr/fonts/Marianne-Regular_Italic.woff2" as="font" crossorigin="anonymous" />-->
<link rel="preload" href="/dsfr/fonts/Marianne-Medium.woff2" as="font" crossorigin="anonymous" />
<!--<link rel="preload" href="/dsfr/fonts/Marianne-Medium_Italic.woff2" as="font" crossorigin="anonymous" />-->
<link rel="preload" href="/dsfr/fonts/Marianne-Bold.woff2" as="font" crossorigin="anonymous" />
<!--<link rel="preload" href="/dsfr/fonts/Marianne-Bold_Italic.woff2" as="font" crossorigin="anonymous" />-->
<!--<link rel="preload" href="/dsfr/fonts/Spectral-Regular.woff2" as="font" crossorigin="anonymous" />-->
<!--<link rel="preload" href="/dsfr/fonts/Spectral-ExtraBold.woff2" as="font" crossorigin="anonymous" />-->