@codegouvfr/react-dsfr

repository·main·Indexed 19 days ago

https://github.com/codegouvfr/react-dsfr

A React-based toolkit for implementing the French State Design System (DSFR). It provides type-safe, SSR-friendly, and tree-shakable components compliant with official DSFR standards. The library includes a CLI for managing static assets and optimizing icon usage, and is compatible with Next.js, Vite, and Create React App. Intended exclusively for official French public service websites.

Tokens
20.6K
Snippets
60
Records
83
Agent score
64%

What's inside @codegouvfr/react-dsfr

  1. Introduction to React-DSFR

    main

    React-DSFR is the React toolkit for the French State Design System.

    It provides a comprehensive set of components designed to align with the official French government design standards. Developers can use the Storybook documentation to:

    • Browse available components.
    • Preview components in various states.
    • Copy and paste code snippets directly into their projects.
    • Use the color helper tool to navigate the DSFR color palette and select appropriate shades for their use cases.
  2. Overview of @codegouvfr/react-dsfr

    main

    The @codegouvfr/react-dsfr package is a React integration for the French Government Design System (DSFR). It provides a toolkit of advanced, type-safe React components that leverage the vanilla JS/CSS implementation of @gouvfr/dsfr.

    Key Features:

    • Type-Safe API: Fully documented and type-safe, though TypeScript usage is optional.
    • DSFR Compliance: Always up-to-date with the latest DSFR evolutions, ensuring the same look and feel as the official @gouvfr/dsfr.
    • SSR Ready: Optimized for Server-Side Rendering (SSR) to prevent white flashes during reload. Most components are ready for Server Components, while others are marked with "use client".
    • Framework Compatibility: Seamless integration with Next.js (both Pages and App directories), Create React App, and Vite.
    • Modular Distributions: Three modular distributions allow you to import only the components you need, preventing large bundle sizes.
    • Advanced Integrations: Supports optional integration with MUI (adapting MUI components to look like DSFR), CSS-in-JS, i18n (internationalization), and routing libraries like react-router.

    WARNING: This design system is intended exclusively for use on official French public service websites to facilitate citizen identification of government sites.

  3. Implement signals with StatefulObservable

    main

    Use StatefulObservable to implement a signal pattern that allows you to wire non-React functions to React components without depending on external libraries like EVT.

    It works by creating an observable value that can be updated outside of the React lifecycle, and then using a hook to trigger re-renders in React components whenever that value changes.

    Key behaviors:

    • Initialization: When created, it takes an initializer function.
    • Attachment: Unlike StatefulEvt, StatefulObservable does not post a value immediately upon the first attachment.
    • Evaluation: If the current value has not yet been evaluated, next() is called on the initial value returned by the initializer function.
    import { createStatefulObservable, useRerenderOnChange } from "tools/StatefulObservable";
    
    // 1. Create the observable with an initial value function
    const $counter = createStatefulObservable(() => 0);
    
    // 2. Update the value from anywhere (non-React code)
    export function incrementCounter() {
        $counter.current++;
    }
    
    // 3. Use the hook in a component to subscribe to changes
    export function Counter() {
        useRerenderOnChange($counter);
    
        const counter = $counter.current;
    
        return <span>Counter: {counter}</span>;
    }
  4. Development setup and commands

    main

    If you are developing on the @codegouvfr/react-dsfr repository, use the following commands to manage the environment, run Storybook, test applications, and execute tests.

    Setup:

    git clone https://github.com/codegouvfr/react-dsfr
    cd react-dsfr
    yarn

    Running Storybook:

    yarn storybook

    Running Test Applications: These commands allow you to test the library in different environments:

    • Create React App: yarn start-cra
    • Vite: yarn start-vite
    • Next.js 13 PagesDir (default): yarn start-next-pagesdir
    • Next.js 13 AppDir: yarn start-next-appdir

    Running Tests:

    • Run all unit tests (test/runtime): yarn test
    • Run a specific test (e.g., CSS variable resolution): npx vitest -t "Resolution of CSS variables"
    # Setup
    git clone https://github.com/codegouvfr/react-dsfr
    cd react-dsfr
    yarn
    
    # Storybook
    yarn storybook
    
    # Test Apps
    yarn start-cra
    yarn start-vite
    yarn start-next-pagesdir
    yarn start-next-appdir
    
    # Tests
    yarn test
    npx vitest -t "Resolution of CSS variables"
  5. How copy-dsfr-to-public works

    main

    The copy-dsfr-to-public utility automates the management of DSFR static assets. It performs the following steps:

    1. Locates Assets: It finds the @gouvfr/dsfr package within your node_modules to extract the source CSS and assets.
    2. Identifies Required Files: It parses dsfr.min.css to identify all assets (images, fonts, etc.) referenced via url() functions, ensuring only actually used assets are copied.
    3. Copies to Public: It creates a /dsfr directory inside your project's public folder (detected via index.html or standard patterns) and populates it with the required files.
    4. Version Management: It writes a version.txt file inside the /dsfr folder containing the version of @gouvfr/dsfr used. If the version in version.txt matches the installed version, the script exits early to save time.
    5. Cache Busting: It automatically modifies your index.html to append a version query parameter (e.g., ?v=1.2.3) to any hrefs pointing to the /dsfr/ directory, helping to prevent stale asset caching.
  6. Configure ButtonsGroup layout and alignment

    main

    The ButtonsGroup component has two primary layout modes determined by the inlineLayoutWhen prop:

    1. Always Stacked (Vertical): Set inlineLayoutWhen="never" (the default). In this mode, alignment can be "left", "center", or "right".
    2. Inline Layout: Set inlineLayoutWhen="always" or a breakpoint like "sm and up", "md and up", or "lg and up". In this mode, alignment can be "left", "center", "right", or "between".

    Additional layout props:

    • isReverseOrder: (Boolean, default false) Reverses the order of buttons in inline layout.
    • buttonsEquisized: (Boolean, default false) Makes all buttons in the group have equal width.
  7. How to choose shadow elevation levels

    main

    When applying shadows to a use-case or context, follow these elevation guidelines:

    • Raised: Use SM (Small) shadows (either Light or Dark depending on context).
    • Overlap: Use MD (Medium) shadows (either Light or Dark depending on context).
    • Lifted: Use LG (Large) shadows (either Light or Dark depending on context).
  8. Handle Range component error and success states

    main

    The Range component supports visual feedback for validation using the state and stateRelatedMessage props.

    • Error State: Set state="error". The component will apply error styling (e.g., fr-range-group--error) and display the stateRelatedMessage. The native input will also receive aria-invalid="true".
    • Success State: Set state="success". The component will apply success styling (e.g., fr-range-group--valid) and display the stateRelatedMessage.
    • Default State: Set state="default". The stateRelatedMessage will not be displayed.
    import { Range } from '@codegouvfr/react-dsfr';
    
    // Error state example
    <Range
      label="Age"
      min={0}
      max={120}
      state="error"
      stateRelatedMessage="Please enter a valid age."
    />
  9. Configure the Newsletter in Follow

    main

    The newsletter prop can be configured in two ways:

    1. With a Form: Requires a form object. You must provide a formComponent which is a function that receives children (the input and button) and returns a React node. This allows you to wrap the input in your own <form> element. You can also control the success state via the success boolean to show a success alert.

    2. Without a Form: A simpler version where only buttonProps are required, rendering a standalone button (as a button or anchor).

    Key NewsletterForm properties:

    • success: boolean to trigger the success alert.
    • successMessage: ReactNode to customize the success message.
    • formComponent: A component that wraps the input and button.
    • consentHint: ReactNode for the legal/consent text below the input.
    • inputProps: Partial configuration for the email input (label, hint, placeholder, etc.).
    <Follow
      newsletter={{
        title: "Newsletter Title",
        buttonProps: { children: "Subscribe" },
        form: {
          success: false,
          formComponent: ({ children }) => <form action="/api/subscribe" method="POST">{children}</form>,
          consentHint: "By subscribing, you agree to our terms.",
        },
      }}
    />
  10. Configure Tag component variants and modes

    main

    The Tag component uses a discriminated union for its props to ensure type safety based on the as prop and the presence of specific functional props like linkProps or iconId.

    Mode Constraints

    • Anchor Mode: Triggered by as="a" or providing linkProps. Requires linkProps to be defined. onClick and nativeButtonProps are not allowed.
    • Button Mode: Triggered by as="button". Supports onClick, dismissible, and pressed. Requires linkProps to be undefined.
    • Paragraph Mode (Default): Triggered by as="p". Supports nativeParagraphProps.
    • Span Mode: Triggered by as="span". Supports nativeSpanProps.

    Deprecation Notice

    nativeSpanProps is deprecated when used with the default paragraph mode. Use nativeParagraphProps instead.

  11. Use the Card component

    main

    The Card component is a versatile container used to group related content. It supports various layouts (vertical, horizontal), image integration (via URL or custom component), and clickable states.

    Key Layout Options

    • Vertical (Default): Standard stacked layout.
    • Horizontal: Uses the horizontal={true} prop. You can control the content/image ratio using the ratio prop with values '33/66' or '50/50' (mapped to tier and half classes).
    • Image Integration:
      • Use imageUrl and imageAlt for a standard <img> tag.
      • Use imageComponent to pass a custom React node as the image.
    • Clickable Card: Set enlargeLink={true} and provide linkProps to make the entire card act as a link.
    import { Card } from '@codegouvfr/react-dsfr';
    
    // Basic Vertical Card
    <Card 
      title="Card Title" 
      desc="Card description text"
    />
    
    // Horizontal Card with Image URL
    <Card
      title="Horizontal Card"
      horizontal
      ratio="33/66"
      imageUrl="/path/to/image.jpg"
      imageAlt="Description"
    />
    
    // Clickable Card
    <Card
      title="Clickable Card"
      enlargeLink
      linkProps={{ href: '/destination' }}
      iconId="fr-icon-arrow-right"
    />
  12. Integrate @codegouvfr/react-dsfr with Next.js Pages Router

    main

    To use the DSFR design system in a Next.js project using the Pages Router, use createNextDsfrIntegrationApi to generate an integration object. This object provides a Higher-Order Component (HOC) withDsfr to wrap your App component and a dsfrDocumentApi to configure your _document.tsx.

    Configuration Options

    When calling createNextDsfrIntegrationApi, you can pass the following parameters:

    ParameterTypeDefaultDescription
    defaultColorSchemeColorScheme | 'system'RequiredSets the initial color scheme ('light', 'dark', or 'system').
    verbosebooleanfalseEnables verbose logging.
    LinkFunction<a>Custom Link component to use for DSFR links.
    preloadFonts(keyof typeof fontUrlByFileBasename)[][]List of font basenames to preload (only in production).
    doPersistDarkModePreferenceWithCookiebooleanfalseIf true, dark mode preference is persisted in cookies.
    useLang() => string() => 'fr'Function that returns the current language string.
    trustedTypesPolicyNamestring'react-dsfr'Custom policy name for Trusted Types (for CSP).
    doDisableFaviconbooleanfalseIf true, prevents the library from injecting its default favicons.

    Implementation Steps

    1. Wrap your App: Use withDsfr in _app.tsx.
    2. Augment your Document: Use augmentDocumentForDsfr in _document.tsx to handle server-side color scheme detection.
    3. Apply HTML Attributes: Use getColorSchemeHtmlAttributes in _document.tsx to inject data-fr-scheme and data-fr-theme into the <html> tag.
    import { createNextDsfrIntegrationApi } from '@codegouvfr/react-dsfr/next-pagesdir';
    
    const { withDsfr, dsfrDocumentApi } = createNextDsfrIntegrationApi({
      defaultColorScheme: 'system',
      doPersistDarkModePreferenceWithCookie: true,
      preloadFonts: ['some-font-basename'],
    });
    
    // In _app.tsx
    export default withDsfr(function App(props) {
      return <Component {...props} />;
    });
    
    // In _document.tsx
    // Use dsfrDocumentApi.augmentDocumentForDsfr and dsfrDocumentApi.getColorSchemeHtmlAttributes