Skip to content

Adapters ​

An adapter turns @block declarations into typed components for a specific framework. All adapters share a common API — it is described on this page; the pages for individual adapters cover only framework-specific details.

AdapteridTypeWhat it generatesRequirements
Reactreactinlinea component in the .ecss moduleReact 18+
Vuevueinlinea component in the .ecss moduleVue 3.3+
Sveltesveltemodule-based.svelte filesSvelte 4+
SolidJSsolidmodule-based.jsx filessolid-js 1.8+
Astroastromodule-based.astro filesAstro 4/5
Vanilla JS (DOM)dominlineclasses in the .ecss modulebrowser (no framework)

inline — the component code is embedded directly into the compiled .ecss module. module-based — the adapter generates real framework files (.svelte/.astro/.jsx) into the internal .ecss/ directory, and imports of ./X.ecss are redirected to them. In both cases you simply import the component from the .ecss file — the difference is invisible.

Setup ​

The adapter is registered in ecss.config.ts via defineConfig from @ecss/config. defaultAdapter specifies which adapter is applied to .ecss files by default (the value is the adapter's id from the table above):

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

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

Multiple adapters in one project ​

You can register several adapters at once in adapters — for example, Astro for pages and React for interactive islands. The same @block will be available in both frameworks:

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

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

defaultAdapter applies to ordinary ./X.ecss imports. To get the component for a different adapter, append the query suffix ?<id> to the path, where <id> is the adapter identifier from the table above (react, vue, svelte, solid, astro, dom):

astro
---
// Astro page — the default adapter
import { EButton } from './Button.ecss';
---
tsx
// React island — explicitly select the React variant of the same block
import { EButton } from './Button.ecss?react';

Imports with the ?<id> suffix are fully typed as well. The suffix works with any registered adapter regardless of defaultAdapter.

Common API ​

The component name is formed as {prefix}{BlockName} (the default prefix is E): @block Button → EButton. The component is imported directly from the .ecss file.

The params prop ​

The block's parameters are passed through a single params prop (in the DOM adapter, a constructor option). Their types come from the generated {Block}Params interface, so you cannot pass a value that is not part of an @enum.

params is required if the block has at least one required @param (without ?); otherwise it can be omitted.

The as prop ​

By default the component renders as a <div>. The as prop accepts any HTML tag and changes the root element; typing narrows the set of allowed attributes to the chosen tag (as="a" exposes href, as="button" exposes type, and so on).

The exception is Astro: there as works too, but there is no per-tag attribute narrowing (any attribute is accepted).

class, style, and attributes ​

ECSS sets the block's class, the CSS variables from params, and data attributes on the root element. What you pass to the component is merged with these values: your class is added to the block's class, style is added to the CSS variables, and the remaining attributes and handlers land on the root element.

Sub-components (@element) ​

If a @block declares @element, they are available as nested components through static properties of the root: <EButton.Icon>. Sub-components support the same as prop but do not accept params.

The componentNamePrefix option ​

All adapter factories accept a componentNamePrefix option — the component name prefix (the default is 'E'):

ts
reactAdapter({ componentNamePrefix: 'My' }); // → MyButton, MyCard
OptionTypeDefaultDescription
componentNamePrefixstring'E'Component name prefix. The first character must be [A-Z], the rest [a-zA-Z0-9_].

An invalid prefix (one that does not match /^[A-Z][a-zA-Z0-9_]*$/) results in a build-time error.

Which adapter to choose ​

  • Using a framework — pick the adapter for that framework (React / Vue / Svelte / SolidJS / Astro).
  • Building a static site or SSR without client-side JS — Astro (server-rendered, zero JS in the browser by default).
  • Writing plain JS without a framework — Vanilla JS (DOM).

See also ​