Skip to content

React

@ecss/react-adapter generates typed React components from @block and @element declarations. The adapter's id is 'react'.

Installation

sh
npm i -D @ecss/react-adapter
sh
pnpm add -D @ecss/react-adapter
sh
yarn add -D @ecss/react-adapter

Requires react version 18 or higher to be installed.

Setup

The adapter is registered in ecss.config.ts via defineConfig from @ecss/config — the config is not tied to any particular bundler; it is read by the ECSS plugin of your bundler. defaultAdapter sets the adapter applied to .ecss files by default:

ts
// ecss.config.ts
import { defineConfig } from '@ecss/config';
import { reactAdapter } from '@ecss/react-adapter';

export default defineConfig({
  adapters: [reactAdapter()],
  defaultAdapter: 'react',
});

Usage

Components are imported directly from the .ecss file — one per @block. The component name is formed as {prefix}{BlockName} (the default prefix is E): @block ButtonEButton.

tsx
import { EButton } from './Button.ecss';

<EButton as="button" params={{ variant: 'primary' }} onClick={handleClick}>
  Click me
</EButton>;

The params prop

The block's parameters are passed through a single params prop. Their types come from the generated {Block}Params interface (for example ButtonParams), so you cannot pass a non-existent @enum value.

params is required if the block has at least one required @param (without ?); if all parameters are optional or there are none at all, the prop can be omitted.

tsx
<EButton params={{ variant: 'primary' }} /> // variant is required
<ECard />                                    // Card has all parameters optional

The as prop

By default the component renders as a <div>. The as prop accepts any HTML tag and changes the root element. The component is typed with a generic parameter based on the chosen tag, so the set of allowed HTML attributes narrows automatically:

tsx
<EButton as="button" type="submit" params={{ variant: 'primary' }}>Submit</EButton>

<EButton as="a" href="/about" params={{ variant: 'ghost' }}>Link</EButton>

With as="a", href, target, and other <a> attributes are available; with as="button", type, disabled, and so on.

className, style, ref, and the remaining props

  • className — concatenated with the block's class without overriding it;
  • style — merged on top of the CSS variables generated from params;
  • ref — forwarded to the root DOM element;
  • all other props (onClick, aria-*, data-*, …) are forwarded to the root element as is.

Sub-components (@element)

If a @block contains @element, they become nested components through static properties of the root:

tsx
import { EButton } from './Button.ecss';

<EButton params={{ withIcon: true }}>
  <EButton.Icon>
    <svg>{/* … */}</svg>
  </EButton.Icon>
  <EButton.Text>Click me</EButton.Text>
</EButton>;

Sub-components support the same as prop (the default is <div>) but do not accept params.

Options

The factory's only option is componentNamePrefix (the component name prefix, default 'E'): reactAdapter({ componentNamePrefix: 'My' })MyButton. For more details, see the adapters overview.

React version support

The adapter detects the major version of React installed in the project and adjusts the generated code:

  • React 19+ref is passed as a regular prop;
  • React 18 — the component is wrapped in forwardRef.

The version is read from react/package.json relative to the project root and cached. If React cannot be resolved, the adapter prints a warning and uses React 19 as the default.

See also