Skip to content

SolidJS

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

Unlike the React/Vue adapters, the Solid adapter is module-based: it generates real .jsx files into the .ecss/solid/ directory, because JSX is compiled through vite-plugin-solid (only .jsx/.tsx). Imports of ./X.ecss are redirected to them automatically — this is transparent to you.

Installation

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

Requires solid-js version 1.8 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 { solidAdapter } from '@ecss/solid-adapter';

export default defineConfig({
  adapters: [solidAdapter()],
  defaultAdapter: 'solid',
});

Usage

Each @block turns into a SolidJS component that is imported directly from the .ecss file. The component name is formed as {prefix}{BlockName} (the default prefix is E): @block ButtonEButton.

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

function App() {
  return (
    <EButton as="button" params={{ variant: 'primary' }}>
      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 by 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.

class, style, and attributes

ECSS sets the block's class, the CSS variables from params, and data attributes on the root element. What the consumer passes is merged with these values:

  • class — the consumer's class is added to the block's class (Solid uses class, not className);
  • style — merged on top of the CSS variables from params; the consumer can pass either a JSX.CSSProperties object or a CSS string — both are supported;
  • the remaining attributes and handlers (onClick, aria-*, data-*, …) are forwarded to the root element and win on collision with data attributes.

The component is reactive: the block is recomputed when params change.

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'): solidAdapter({ componentNamePrefix: 'My' })MyButton. For more details, see the adapters overview.

Requirements

Requires SolidJS (solid-js) version 1.8 or higher.

The generated components are plain JSX (no TypeScript inside), and the types are attached automatically, so the adapter works the same in both JS and TS projects.

See also