@fibrous/semi-ui-react is the React component library of Semi Design Next.
Requirements
Peer dependencies: react and react-dom ^19.3.0.
| Item | Requirement |
|---|---|
| Browser | Chrome 120, Firefox 125, Safari 17.2 or later. Supporting Safari below 17.4 needs @fibrous/semi-polyfill; see Install below |
| Modules and syntax | ESM only; syntax and built-ins ship as ESNext, without lowering |
| Node.js | 24 or later, since server rendering runs component code; 22.17 or later when Node only runs the build plugins and semi-build-scss |
| Build plugins | Vite 8 or later, webpack 5.77 or later, rspack 2 |
Install
pnpm add @fibrous/semi-ui-react @fibrous/semi-icons-react
The icon package @fibrous/semi-icons-react and the illustration package @fibrous/semi-illustrations-react are dependencies of the component library. When your code imports them directly, declare them in your own package.json; pnpm does not allow importing undeclared packages.
With npm or yarn, use the equivalent commands.
To support Safari below 17.4, also install @fibrous/semi-polyfill and import it on the first line of the app entry, before any Semi component. It only adds AbortSignal.any and Element.prototype.checkVisibility, and does nothing where the browser has them natively; to add just one of them, import @fibrous/semi-polyfill/abort-signal-any or @fibrous/semi-polyfill/check-visibility.
import '@fibrous/semi-polyfill';
Import the tokens
The components import the theme tokens (the --semi-* CSS variables) and the base rules themselves, so pages render correctly without this step. When the first component is lazy loaded, though, the tokens only apply once that component's chunk has loaded. Import the tokens entry first in your app's entry file and the tokens load with the entry's CSS, so your own styles can use the --semi-* variables from the first paint.
// main.tsx, before any other import
import '@fibrous/semi-ui-react/tokens';
The tokens entry is a JS module that imports the same tokens the components use, so the build output holds one copy, and build plugin options such as prefixCls apply to it as well. With omitCss it is removed along with the component styles, and the full stylesheet provides the tokens.
Use components
import { Button, Toast } from '@fibrous/semi-ui-react';
export function App() {
return <Button onClick={() => Toast.warning({ content: 'welcome' })}>Hello Semi</Button>;
}
A single component can also be imported by its directory path, written down to /index, such as import Button from '@fibrous/semi-ui-react/button/index'.
Styles and tokens load on demand with the components, and only the components you use are bundled. Semi ships box-sizing: border-box and the scrollbar base styles itself, so a reset stylesheet such as reset.css is not needed; an extra reset changes component sizes.
Build plugins
webpack and rspack need no build plugin when you keep the default semi prefix, layered styles and per-component style imports. The components import precompiled CSS with the semi class prefix, wrapped in @layer semi: you need a rule for .css imports, such as css-loader with style-loader (or the mini-css-extract-plugin loader) in webpack, or type: 'css' in rspack. The Vite dev server works without the plugin, but vite build needs @fibrous/semi-vite-plugin, for the reason given below. For hosts that render on the server, see Server-side rendering.
For the options below, use the plugin for your build tool: @fibrous/semi-vite-plugin for Vite, @fibrous/semi-webpack-plugin for webpack 5, and @fibrous/semi-rspack-plugin for rspack 2. The plugins apply to the component library, the icon packages and the AI packages.
| Option | Default | Description |
|---|---|---|
prefixCls | 'semi' | Class prefix, rewritten both in the component JS and in the compiled styles |
cssLayer | true | Whether the styles are wrapped in @layer semi; false outputs unlayered styles |
omitCss | false | Drops the style imports inside the Semi packages so the app loads the full stylesheet instead, see Full stylesheet |
// vite.config.ts
import { defineConfig } from 'vite';
import semi from '@fibrous/semi-vite-plugin';
export default defineConfig({
plugins: [semi({ prefixCls: 'my' })],
});
// webpack.config.mjs
import SemiWebpackPlugin from '@fibrous/semi-webpack-plugin';
export default {
plugins: [new SemiWebpackPlugin({ prefixCls: 'my' })],
};
// rspack.config.mjs; the project needs a rule that treats .scss as CSS
import { SemiRspackPlugin } from '@fibrous/semi-rspack-plugin';
export default {
module: { rules: [{ test: /\.s?css$/, type: 'css' }] },
plugins: [new SemiRspackPlugin({ prefixCls: 'my' })],
};
The webpack plugin also takes extractCssOptions (for example to use the mini-css-extract-plugin loader) and overrideStylesheetLoaders (to append your own loaders to the default chain). If class names are still semi-* in vite dev, the prebundle cache comes from the previous configuration; rebuild it with vite --force.
The published .scss files use semi: URLs that plain Sass cannot resolve; compile them through a build plugin or @fibrous/semi-scss-compile.
Full stylesheet
To load every component's styles at once, import the full stylesheet and turn on the build plugin's omitCss; otherwise the components' own style imports are bundled again and each component's styles appear twice. The full stylesheet includes the tokens and the icon styles, uses the semi class prefix, and is wrapped in @layer semi.
import '@fibrous/semi-ui-react/styles.css';
// with the AI components, import theirs after it
import '@fibrous/semi-ai-react/styles.css';
With omitCss the plugins no longer compile styles, so cssLayer has no effect, and the webpack and rspack plugins throw when both are passed. For a full stylesheet with a custom prefix or without layers, generate one with @fibrous/semi-scss-compile: --prefix matches the plugin's prefixCls, and --no-layer outputs unlayered styles. -f and -i are optional and default to the foundation and icons-core that @fibrous/semi-scss-compile itself depends on, so its version must match the component library; when it does not, point -f and -i to the dist of the @fibrous/semi-foundation and @fibrous/semi-icons-core installed in your app, which pnpm requires as direct dependencies. --ai always needs a path, to the dist of @fibrous/semi-ai-foundation; with pnpm add it as a direct dependency at the same version as the component library.
pnpm add -D @fibrous/semi-scss-compile
pnpm exec semi-build-scss -o src/semi.css --min --prefix my
# with the AI components, generate a second file and import it after the first (also install @fibrous/semi-ai-foundation)
pnpm add -D @fibrous/semi-ai-foundation
pnpm exec semi-build-scss --ai node_modules/@fibrous/semi-ai-foundation/dist -o src/semi-ai.css --min --prefix my
Customize styles
Customizing styles needs no build plugin: override the --semi-* CSS variables in your own stylesheet to change the global colors, radii, font sizes, spacing and so on (see Design Tokens for the list, and Dark Mode for dark values). Semi's styles sit in @layer semi, so your unlayered CSS rules always win and can override a component's own styles without !important.
:root {
--semi-color-primary: var(--semi-violet-5);
}
.my-toolbar .semi-button {
border-radius: 0;
}
When using Tailwind, see Tailwind for the layer order and for using Semi tokens in utility classes.
Icons and illustrations
import { IconHome } from '@fibrous/semi-icons-react';
export function HomeIcon() {
return <IconHome aria-label="Home" />;
}
Illustrations are imported from @fibrous/semi-illustrations-react, such as IllustrationNoContent.
Icon styles load with the icon components. See Icon for the icon list and usage.
AI components
The AI components (AIChatInput, AIChatDialogue, Chat, Sidebar and others) live in a separate AI package built on the component library, and their styles load with the components. When loading full stylesheets, import the AI package's after the component library's. See AI components for an overview.
pnpm add @fibrous/semi-ai-react
Server-side rendering
For server-side rendering and hydration, configure the host as the table below shows; most hosts also need the Semi build plugin. By default styles load per component: the component JS imports its own styles and you do not import styles.css. The full stylesheet is an optional route: turn on the build plugin's omitCss and import styles.css once, see Full stylesheet. Do not mix the two routes; mixed, the CSS roughly doubles.
| Host | Install | Host configuration | Styles |
|---|---|---|---|
Next.js 16 App Router, --webpack | @fibrous/semi-ui-react | none | none |
| Next.js 16 default Turbopack, Pages Router, or both Routers mixed | also -D @fibrous/semi-next | export default semiNext()({ ... }) | none |
| Next.js with a custom prefix | also -D @fibrous/semi-next @fibrous/semi-scss-compile | semiNext({ prefixCls: 'my', omitCss: true }) | import the stylesheet from semi-build-scss --prefix my once |
React Router 8 framework mode (including serverBundles) | @fibrous/semi-ui-react, -D @fibrous/semi-vite-plugin | plugins: [semi(), reactRouter()] | none |
// next.config.mjs, Turbopack and Pages Router
import semiNext from '@fibrous/semi-next';
export default semiNext()({
// your Next.js configuration
});
// vite.config.ts, React Router
import { reactRouter } from '@react-router/dev/vite';
import { defineConfig } from 'vite';
import semi from '@fibrous/semi-vite-plugin';
export default defineConfig({
plugins: [semi(), reactRouter()],
});
With a custom prefix in Next.js, prefixCls must come with omitCss: true. Generate the stylesheet with semi-build-scss and import it once in the root layout (pages/_app for the Pages Router):
// next.config.mjs
import semiNext from '@fibrous/semi-next';
export default semiNext({ prefixCls: 'my', omitCss: true })({});
pnpm add -D @fibrous/semi-next @fibrous/semi-scss-compile
pnpm exec semi-build-scss --prefix my -o app/semi-my.css --min
// app/layout.jsx
import './semi-my.css';
React Router's RSC mode takes the same plugin: plugins: [semi(), reactRouterRSC(), rsc()] (reactRouterRSC is unstable_reactRouterRSC from @react-router/dev/vite, rsc comes from @vitejs/plugin-rsc). In Server Components, Next.js App Router and React Router's RSC mode can import the flat components and a few sub-component named exports directly (such as TypographyTitle; the dot syntax Typography.Title is undefined in a Server Component). semiGlobal, Form's HOC and hooks, and the locale passed to ConfigProvider can only be used or imported in 'use client' files.
Common conventions:
- Import locales from the
locale/source/subpath of the UI package (such aslocale/source/en_US); you do not need to install@fibrous/semi-foundationdirectly. - Set
semiGlobalonly at app startup; useConfigProviderfor configuration that changes per request. - Overlays that are open by default appear after hydration.
- CodeHighlight bundles no languages: install
shikidirectly in your app (pnpm does not allow importing undeclared packages) and pass languages fromshiki/langs/*throughCodeHighlight.registerorlanguages, see CodeHighlight.