@fibrous/semi-ui-solid is the Solid component library of Semi Design Next; for server-side rendering see Server-side rendering.
Requirements
Peer dependency: solid-js ^1.9.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-solid @fibrous/semi-icons-solid
The icon package @fibrous/semi-icons-solid is a dependency of the component library; when your code imports it directly, declare it in your own package.json, since pnpm does not allow importing undeclared packages. The illustration package @fibrous/semi-illustrations-solid is not installed with the library; install it when you need it.
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.
// index.tsx, before any other import
import '@fibrous/semi-ui-solid/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-solid';
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-solid/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.
Solid projects differ as follows:
- Vite: the dev server needs
@fibrous/semi-vite-plugintoo, asplugins: [solid(), semi()]. vite-plugin-solid keeps the Solid packages out of the pre-bundle, so foundation's CommonJS dependencies (such asstyle-to-js) reach the browser unconverted and the page stays blank; the plugin adds them to the pre-bundle. - webpack 5 and rspack 2: Semi's webpack and rspack plugins do not process the Solid packages, so the plugin options (
prefixCls,cssLayer: falseandomitCss) have no effect on them; only the default prefix and layered styles are supported, and neither plugin is used. Compile the app's JSX withbabel-loaderandbabel-preset-solid(needs@babel/core7); the bundler does not recognise thesolidexport condition and takes thedefaultcondition,dist/*.js, which is already compiled for the DOM. Components import their own styles: webpack needscss-loaderandstyle-loader, rspack needstype: 'css'.
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-solid/styles.css';
// with the AI components, import theirs after it
import '@fibrous/semi-ai-solid/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-solid';
export function HomeIcon() {
return <IconHome aria-label="Home" />;
}
Illustrations are imported from @fibrous/semi-illustrations-solid, 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-solid
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 |
|---|---|---|---|
| SolidStart 2 | @fibrous/semi-ui-solid, -D @fibrous/semi-vite-plugin | plugins: [solidStart(), nitro(), semi({ omitCss: true })] | import @fibrous/semi-ui-solid/styles.css once in app.tsx |
// vite.config.ts
import { solidStart } from '@solidjs/start/config';
import { nitro } from 'nitro/vite';
import { defineConfig } from 'vite';
import semi from '@fibrous/semi-vite-plugin';
export default defineConfig({
plugins: [solidStart(), nitro(), semi({ omitCss: true })],
});
// src/app.tsx
import '@fibrous/semi-ui-solid/styles.css';
SolidStart uses the full stylesheet because by default its dev server does not collect the CSS that components inside node_modules import: the per-component route works in production, but the first paint in dev has no styles. Passing css: { filter: { exclude: [/^(?!.*\/node_modules\/@fibrous\/semi-).*\/node_modules\//] } } to solidStart() makes dev collect the component CSS of the Semi packages too, at the cost of one more host option and a dev first paint whose styles are slightly larger than the full stylesheet (about 20% more), split into dozens of <style> elements.
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.