For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt, and this page is available as Markdown at /guide/basic/css.md.
close
  • English
  • CSS

    Rstest can either process styles with the Rsbuild toolchain or replace style imports when a test does not depend on them. Choose the setup based on what the test needs to verify:

    Test goalRecommended setup
    Test component logic without checking stylesUse the default style handling
    Use CSS Modules class names or verify preprocessor compilationProcess styles in Node.js tests
    Check computed styles, layout, or visual behaviorUse Browser Mode

    Process styles in tests

    Node.js tests

    Rstest has built-in support for CSS and CSS Modules. In Node.js tests, including jsdom and happy-dom, it processes CSS without emitting CSS assets by default:

    • Regular CSS files do not produce style assets.
    • CSS Modules export class name mappings that component tests can use.
    • Without a matching loader or resource type, ordinary .less, .scss, and .sass imports export an empty string (''). No preprocessor plugin is needed for these imports.
    • When a Less or Sass plugin is enabled, its processing and options remain effective. Compilation errors are still reported.

    The fallback also applies to .module.less, .module.scss, and .module.sass: their default export is {}, so styles.button is undefined. Enable a preprocessor plugin when you need real class name mappings, or replace style imports when you want a property-name proxy.

    User loaders, aliases, and resource types retain their behavior. Imports with a query, such as ?raw or ?url, require a matching plugin or rule and do not use the fallback. Style files must still exist; the fallback does not replace unresolved imports.

    For example, .css and .module.css imports work without additional CSS configuration:

    Button.tsx
    import styles from './Button.module.css';
    import './reset.css';
    
    export function Button() {
      return <button className={styles.button}>Submit</button>;
    }
    Button.test.tsx
    import { expect, test } from '@rstest/core';
    import styles from './Button.module.css';
    
    test('loads CSS Modules', () => {
      expect(styles.button).toEqual(expect.any(String));
    });

    CSS Modules commonly use filenames such as .module.css, .module.less, or .module.scss. Use output.cssModules to customize class name generation and other CSS Modules options.

    Add CSS preprocessor support

    To compile preprocessor styles, enable a corresponding Rsbuild plugin. The examples below cover Less and Sass. For another preprocessor, check the Rsbuild plugin list for a matching plugin and register it in the same way. Install only the plugins required by the styles under test.

    @rsbuild/plugin-less compiles .less and .module.less files:

    npm
    yarn
    pnpm
    bun
    deno
    npm add @rsbuild/plugin-less -D
    rstest.config.ts
    import { pluginLess } from '@rsbuild/plugin-less';
    import { defineConfig } from '@rstest/core';
    
    export default defineConfig({
      plugins: [pluginLess()],
    });

    @rsbuild/plugin-sass compiles .sass, .scss, .module.sass, and .module.scss files:

    npm
    yarn
    pnpm
    bun
    deno
    npm add @rsbuild/plugin-sass -D
    rstest.config.ts
    import { pluginSass } from '@rsbuild/plugin-sass';
    import { defineConfig } from '@rstest/core';
    
    export default defineConfig({
      plugins: [pluginSass()],
    });

    If your project reuses an Rsbuild config through @rstest/adapter-rsbuild, configure these plugins in rsbuild.config.ts instead of duplicating them in rstest.config.ts.

    Warning

    If you use @rstest/adapter-rspack, the Rsbuild plugin and output.cssModules examples above do not apply. This adapter uses the CSS rules from rspack.config.ts, so configure Less, Sass, and CSS Modules there.

    Browser mode

    Node.js tests can verify imported values, but styles do not participate in page rendering. Use Browser Mode when a test needs computed styles, layout, or visual behavior. Browser Mode runs the component and its styles in a real browser. It does not use the Node.js empty-style fallback; Less and Sass require their corresponding plugin or loader.

    Replace styles in logic tests

    If a Node.js test only checks component logic, it can replace style imports with test substitutes. This avoids installing a Less or Sass plugin and skips CSS preprocessing, but it also means the test cannot validate the replaced styles.

    Replace specific imports with an alias

    For a small number of known CSS Modules imports, use resolve.alias with identity-obj-proxy. The package returns each requested property name as its value, so styles.button returns button.

    npm
    yarn
    pnpm
    bun
    deno
    npm add identity-obj-proxy -D
    rstest.config.ts
    import { createRequire } from 'node:module';
    import { defineConfig } from '@rstest/core';
    
    const require = createRequire(import.meta.url);
    
    export default defineConfig({
      resolve: {
        alias: {
          './Button.module.less$': require.resolve('identity-obj-proxy'),
        },
      },
    });

    The $ suffix makes the alias match the complete import request. It is a marker in the alias key and is not part of the import path.

    resolve.alias matches string prefixes, not regular expressions. Use it when the source has a small, stable set of style import requests. The alias can also point to a local stub module; for example, an empty module is enough for a side-effect-only import such as import './reset.less'.

    Replace style imports by extension

    For many style imports, use NormalModuleReplacementPlugin to replace files by extension. The following Node.js setup replaces both CSS Modules and side-effect-only style imports with identity-obj-proxy:

    rstest.config.ts
    import { defineConfig } from '@rstest/core';
    
    export default defineConfig({
      tools: {
        rspack(config, { rspack, isServer }) {
          if (!isServer) {
            return;
          }
    
          config.plugins.push(
            new rspack.NormalModuleReplacementPlugin(
              /\.(css|less|sass|scss)(?:\?.*)?$/,
              (resource) => {
                resource.request = 'identity-obj-proxy';
              },
            ),
          );
        },
      },
    });

    Both replacement approaches bypass style resolution and compilation. Keep their tradeoffs in mind:

    • styles.button returns button, not the generated class name from a real build.
    • Less, Sass, and CSS Modules syntax is not validated.
    • Missing or misspelled style files are not detected because the import is replaced before the original file is resolved.
    • Features such as composes, :global, emitted styles, and client/server class name consistency cannot be tested.

    Use real style processing when any of these behaviors matter, and use Browser Mode when the assertion depends on rendered styles.