Skip to content
WEB COMPONENTS FRAMEWORK

NativeWebComponentsFramework

TypeScript components. Familiar templates. Standard Custom Elements.

How it works

COMPONENT FIRST

Built for components

Definition, communication, and reuse all begin with the component boundary.
01

Vue-style templates

Use v-if, v-for, v-model, and event bindings without introducing VNodes.

02

TypeScript file components

Keep logic, template, and styles in one TypeScript file; compile at build time.

03

Chain as an extension

Bring in @elfui/chain only when a builder or runtime string template is useful.

04

No virtual DOM

Static structure is known at build time. Runtime never diffs or patches a component tree.

05

Component inheritance

Derive design-system variants from a base component instead of duplicating an implementation.

06

Native Web Components

Ship standard Custom Elements that can register and run in any frontend stack.

COMPONENT INHERITANCE

One Button definition, two component styles

Reuse template, interaction, and base styles; change only the semantic role or design tokens.
Button.variants.ts
import { useExtend, useVariant } from "@elfui/core";

export const PrimaryButton = useExtend(Button)
  .name("primary-button")
  .build();

export const DangerButton = useVariant(
  Button, "danger-button", setDangerTone
).build();
<elf-button>

Shared props, events, and base styles

useExtend()<primary-button>

Semantic component

Name the derived tag and append focused local styles.

useVariant()<danger-button>

Design-system variant

Keep Button behavior and adjust only tone and visual semantics.

ARCHITECTURE

Learn from the best ideas

Learn from mature authoring models while keeping the boundary focused on native Web Components with templates.
ConcernElfUIVueReactLitSolid
Update modelCompile-time fine-grained DOMReactivity + VDOMVDOM reconciliationTemplate partsFine-grained DOM
Primary authoringTS + HTML macroSFC templatesJSXTagged templatesJSX
Runtime workNo VNodes or patchComponent runtimeReconciler runtimeSmall template runtimeFine-grained runtime
OutputStandard Custom ElementsVue componentsReact componentsWeb ComponentsJSX components
Component boundaryBrowser-native tagsFramework componentFramework componentBrowser-native tagsFramework component

SIZE SNAPSHOT

Choose only the entry you need

10.52 KB@elfui/coregzip, no runtime compiler
15.00 KBcore + routergzip, including router
21.19 KB@elfui/chaingzip, runtime compiler included

BROWSER BENCHMARK

Median time in a real browser

1.10 msmount 300 components
1.50 mscreate a 1k list
0 KBmemory smoke retained

Size data is from the 2026-07-11 release-readiness report. Browser figures are same-day smoke medians for ElfUI regression tracking, not a cross-framework ranking.

CORE PRINCIPLES

Core principles

Dynamic points are split at build time. No VNodes and no component-tree diff at runtime.

Counter.ts
import { defineHtml, useRef }
  from "@elfui/core";

const count = useRef(0);
const increment = () =>
  count.set(count.peek() + 1);

export const Counter = defineHtml(`
  <button @click=${increment}>
    Count: ${count}
  </button>
`);
01
Parse template

Find text, attributes, events, branches, and lists.

02
Split dynamic points

Generate an isolated update path for every state read.

03
Bind effects

Subscribe only to the state each dynamic point consumes.

04
Emit native output

Write real DOM and register a standard Custom Element.

0 VNodeDirect DOM writes
Fine-grainedOne subscription per dynamic point
Custom ElementsStandards-based output