@element-hq/web-shared-components

中文文档
0.2.3发布于 2026/11/03TypeScriptESM + CJS构建来源已验证近 30 天下载 2659

Shared components for Element

原始使用文档
文档目录17 节
  1. Installation in a new project
  2. Usage
  3. Basic Import
  4. Sub-path Imports
  5. Using Components
  6. Utilities
  7. Development
  8. Prerequisites
  9. Setup
  10. Running Storybook
  11. Write components
  12. Write Storybook Stories
  13. Tests
  14. Unit Tests
  15. Visual Regression Tests
  16. Translations
  17. Publish a new version

@element-hq/web-shared-components

Online storybook

Shared React components library for Element Web, Aurora, Element modules... This package provides opinionated UI components built on top of the Compound Design System and Compound Web. This is not a design system by itself, but rather a set of larger components.

Installation in a new project

When adding this library to a new project, as well as installing @element-hq/web-shared-components as normal, you will also need to add compound-web as a peer dependency:

pnpm add @element-hq/web-shared-components
pnpm add @vector-im/compound-web

(This avoids problems where we end up with different versions of compound-web in the top-level project and web-shared-components).

Usage

Basic Import

Both JavaScript and CSS can be imported as follows:

import { RoomListHeaderView, useViewModel } from "@element-hq/web-shared-components";
import "@element-hq/web-shared-components/dist/element-web-shared-components.css";

or in CSS file:

@import url("@element-hq/web-shared-components");

Sub-path Imports

Callers running outside the browser DOM (e.g. inside an AudioWorkletGlobalScope or a worker) can pull in the small standalone numbers utility bundle without loading the rest of the package bundle, which transitively imports React, dnd-kit, and other code that touches window / document:

import { percentageOf, percentageWithin } from "@element-hq/web-shared-components/numbers";

The sub-path exposes the same functions listed under Formatting and ships as its own ES/CJS bundle in dist/numbers.{js,umd.cjs}. Prefer the main package entry for everything else.

Using Components

There are two kinds of components in this library:

  • regular react component which doesn't follow specific pattern.
  • view component(MVVM pattern).

[!TIP] These components are available in the project storybook.

Regular Components

These components can be used directly by passing props. Example:

import { Flex } from "@element-hq/web-shared-components";
function MyApp() {
    return <Flex align="center" />;
}

View (MVVM) Components

These components follow the MVVM pattern. A ViewModel instance should be provided as a prop.

Here's a basic example:

import { ViewExample } from "@element-hq/web-shared-components";

function MyApp() {
    const viewModel = new ViewModelExample();
    return <ViewExample vm={viewModel} />;
}

Utilities

Internationalization

  • useI18n() - Hook for translations
  • I18nApi - Internationalization API utilities

Date & Time

  • DateUtils - Date formatting and manipulation
  • humanize - Human-readable time formatting

Formatting

  • FormattingUtils - Text and data formatting utilities
  • numbers - Number formatting utilities

Development

Prerequisites

  • Node.js >= 20.0.0
  • pnpm => 10

Setup

# Install dependencies
pnpm install

# Build the library
pnpm prepack

Running Storybook

pnpm storybook

Write components

Most components should be written as MVVM pattern view components. See existing components for examples. The exceptions are low level components that don't need a view model.

Write Storybook Stories

All components should have accompanying Storybook stories for documentation and visual testing. Stories are written in TypeScript using the Component Story Format (CSF).

Use shallow, browse-oriented story titles such as RoomList/RoomListSearchView or TimelineBody/DecryptionFailureBodyView. Do not mirror the full source path in the Storybook title.

Story File Structure

Place the story file next to the component with the .stories.tsx extension:

MyComponent/
├── MyComponent.tsx
├── MyComponent.module.css
└── MyComponent.stories.tsx

Regular Component Stories

For regular React components (non-MVVM), create stories by defining a meta object and story variations:

import type { Meta, StoryObj } from "@storybook/react-vite";
import { fn } from "storybook/test";
import { MyComponent } from "./MyComponent";

const meta = {
    title: "Category/MyComponent",
    component: MyComponent,
    tags: ["autodocs"],
    args: {
        // Default args for all stories
        label: "Default Label",
        onClick: fn(), // Mock function for tracking interactions
    },
} satisfies Meta<typeof MyComponent>;

export default meta;
type Story = StoryObj<typeof meta>;

// Default story uses the default args
export const Default: Story = {};

// Override specific args for variations
export const WithCustomLabel: Story = {
    args: {
        label: "Custom Label",
    },
};

export const Disabled: Story = {
    args: {
        disabled: true,
    },
};

MVVM Component Stories

For MVVM components, create a wrapper component that uses useMockedViewModel and withViewDocs:

import React, { type JSX } from "react";
import { fn } from "storybook/test";
import type { Meta, StoryObj } from "@storybook/react-vite";
import { MyComponentView, type MyComponentViewSnapshot, type MyComponentViewActions } from "./MyComponentView";
import { useMockedViewModel } from "../../viewmodel";
import { withViewDocs } from "../../../.storybook/withViewDocs";

// Combine snapshot and actions for easier typing
type MyComponentProps = MyComponentViewSnapshot & MyComponentViewActions;

// Wrapper component that creates a mocked ViewModel.
// Must be a named variable (not inline) for docgen to extract its props.
const MyComponentViewWrapperImpl = ({ onAction, ...rest }: MyComponentProps): JSX.Element => {
    const vm = useMockedViewModel(rest, {
        onAction,
    });
    return <MyComponentView vm={vm} />;
};
// withViewDocs copies the View's JSDoc description onto the wrapper for Storybook autodocs
const MyComponentViewWrapper = withViewDocs(MyComponentViewWrapperImpl, MyComponentView);

// Must use `satisfies` (not `as` or `: Meta`) to preserve type info for docgen
const meta = {
    title: "Category/MyComponentView",
    component: MyComponentViewWrapper,
    tags: ["autodocs"],
    args: {
        // Snapshot properties (state)
        title: "Default Title",
        isLoading: false,
        // Action properties (callbacks)
        onAction: fn(),
    },
} satisfies Meta<typeof MyComponentViewWrapper>;

export default meta;
type Story = StoryObj<typeof MyComponentViewWrapper>;

export const Default: Story = {};

export const Loading: Story = {
    args: {
        isLoading: true,
    },
};

Thanks to this approach, we can directly use primitives in the story arguments instead of a view model object.

[!IMPORTANT] Three requirements must be met for snapshot field documentation to appear in Storybook's ArgTypes table:

  1. Named wrapper variable — the wrapper must be assigned to a named const (e.g. MyComponentViewWrapperImpl) before being passed to withViewDocs, so that react-docgen-typescript can extract its props.
  2. withViewDocs call — wraps the wrapper component with the original View to copy the View's JSDoc description.
  3. satisfies Meta — the meta object must use satisfies Meta<...> (not as Meta<...> or : Meta<...> =). Type assertions and annotations erase the inferred component type that docgen relies on.

Linking Figma Designs

This package uses @storybook/addon-designs to embed Figma designs directly in Storybook. This helps developers compare their implementation with the design specs.

  1. Get the Figma URL: Open your design in Figma, click "Share" → "Copy link"
  2. Add to story parameters: Include the design object in the meta's parameters
  3. Supported URL formats:
    • File links: https://www.figma.com/file/...
    • Design links: https://www.figma.com/design/...
    • Specific node: https://www.figma.com/design/...?node-id=123-456

Example with Figma integration:

const meta = {
    title: "RoomList/RoomListSearchView",
    component: RoomListSearchViewWrapper,
    tags: ["autodocs"],
    args: {
        // ... your args
    },
    parameters: {
        design: {
            type: "figma",
            url: "https://www.figma.com/design/vlmt46QDdE4dgXDiyBJXqp/ER-33-Left-Panel?node-id=98-1979",
        },
    },
} satisfies Meta<typeof RoomListSearchViewWrapper>;

export default meta;

The Figma design will appear in the "Design" tab in Storybook.

Non-UI Utility Stories

For utility functions, helpers, and other non-UI exports, create documentation stories using TSX format with TypeDoc-generated markdown.

src/core/utils/humanize.stories.tsx

import React from "react";
import { Markdown } from "@storybook/addon-docs/blocks";

import type { Meta } from "@storybook/react-vite";
import humanizeTimeDoc from "../../typedoc/functions/humanizeTime.md?raw";

const meta = {
    title: "Core/Humanize",
    parameters: {
        docs: {
            page: () => (
                <>
                    <h1>humanize</h1>
                    <Markdown>{humanizeTimeDoc}</Markdown>
                </>
            ),
        },
    },
    tags: ["autodocs", "skip-test"],
} satisfies Meta;

export default meta;

// Docs-only story - renders nothing but triggers autodocs
export const Docs = {
    render: () => null,
};

[!NOTE] Be sure to include the skip-test tag in your utility stories to prevent them from running as visual tests.

Workflow:

  1. Write TsDoc in your utility function
  2. Export the function from src/index.ts
  3. Run pnpm build:doc to generate TypeDoc markdown
  4. Create a .stories.tsx file importing the generated markdown
  5. The documentation appears automatically in Storybook

Tests

Two types of tests are available: unit tests and visual regression tests.

Unit Tests

These tests cover the logic of the components and utilities. Built with Vitest and React Testing Library.

pnpm test:unit

Visual Regression Tests

These tests ensure the UI components render correctly. Built with Storybook and run under vitest using playwright.

pnpm test:storybook:update

Each story will be rendered and a screenshot will be taken and compared to the existing baseline. If there are visual changes or AXE violation, the test will fail.

Screenshots are located in packages/shared-components/__vis__/.

[!IMPORTANT] In case of docker issues with Playwright, see playwright EW documentation.

Translations

First see our translation guide and translation dev guide. To generate translation strings for this package, run:

pnpm i18n

Publish a new version

To carry out a release, see the documentation at ../RELEASING.md.

版本与发布记录

@element-hq/web-shared-components 共发布 21 个版本,首次发布于 2025/10/10;过去 12 个月发布了 14 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.2.3latest2026/11/0310 天前
0.2.22026/10/131 个月前
0.2.12026/10/081 个月前
0.2.02026/09/072 个月前
0.1.22026/07/084 个月前
0.1.12026/06/234 个月前

其他发布通道:test → 0.0.1。

关于 @element-hq/web-shared-components 的常见问题

@element-hq/web-shared-components 是什么?

Shared components for Element

如何安装 @element-hq/web-shared-components?

在项目目录运行 npm install @element-hq/web-shared-components,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(^19.0.0)、react-dom(^19.0.0)、@vector-im/compound-web(^8.3.5 || ^9.0.0 || ^10.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@element-hq/web-shared-components 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@element-hq/web-shared-components 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@element-hq/web-shared-components 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@element-hq/web-shared-components 可以商用吗?

@element-hq/web-shared-components 使用 未声明 许可。package.json 没有声明许可证。没有许可证的代码默认保留全部权利,商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括,不构成法律意见,请以许可原文为准。

@element-hq/web-shared-components 的下载量有多少?

根据 npm 官方下载统计,@element-hq/web-shared-components 在 2026/10/14 – 2026/11/12 这 30 天内被下载 2,659 次,日均约 89 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@element-hq/web-shared-components 还在维护吗?

最新版本 0.2.3 发布于 2026/11/03(10 天前);至今共发布 21 个版本,其中过去 12 个月发布了 14 个;首次发布于 2025/10/10。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。