Skip to content

quick start

This page assumes the project was created by pnpm create elfui@beta my-app --install. The scaffolding has been configured with the Vite plugin and generates src/main.ts, src/App.ts and vite.config.ts.

This page writes a counter component from scratch.

Prerequisites

This page uses ElfUI Macro components. Select Macro when creating the project and keep the Vite plugin configuration generated by the scaffold.

Use ElfUI Language Tools

Install ElfUI Language Tools for template completion, type information on hover, real-time diagnostics, go-to-definition, rename support, and snippets.

Search for ElfUI Language Tools in the VS Code Extensions view, or install it from the link above. It activates automatically when you open an ElfUI component in a .ts, .tsx, .js, or .jsx file.

Try it online

Do not want to set up a local environment first? Open the ElfUI Playground to edit, run, and preview an example in the browser. The link creates your own StackBlitz workspace and leaves the official starter unchanged.

Create component

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

const count = useRef(0);
const inc = (): void => count.set(count.peek() + 1);

export const Counter = defineHtml(`
  <button class="counter" @click=${inc}>Count: ${count}</button>
`);

The top-level code is the component’s setup logic. ${count} in the template will be compiled into responsive text binding, and @click=${inc} will become an event listener.

Current recommended syntax

Use defineHtml(\...`)anddefineStyle(`...`)directly. Beta.7 removes thehtmlandcss` tagged-template helpers; new projects and scaffold output use only the direct forms.

Mount application

ts
// main.ts
import { createApp } from "@elfui/core";
import { Counter } from "./Counter";

createApp(Counter).mount("#app");

index.html only needs an ordinary container and does not require handwritten component tags:

html
<div id="app"></div>

Do not mount the root component twice

createApp(Counter).mount("#app") automatically registers the root component, creates an instance, and replaces the existing container content. Its return value is the actual Custom Element. The export name Counter is still inferred as elf-counter, but the application entry must not write that tag manually.

Register a component in existing HTML

If the component must be written directly into existing HTML, or a component library needs batch global registration, use registerComponents():

ts
import { registerComponents } from "@elfui/core";

registerComponents(Counter);

If you need to customize a single component name, you can use defineName(); if you need to unify the project prefix, please configure tagPrefix in the Vite plug-in.

Add style

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

defineStyle(`
  .counter {
    border: 0;
    border-radius: 8px;
    padding: 8px 12px;
  }
`);

const count = useRef(0);
const inc = (): void => count.set(count.peek() + 1);

export const Counter = defineHtml(`
  <button class="counter" @click=${inc}>Count: ${count}</button>
`);

Styles are isolated by default

Component styles are injected into the current Custom Element's Shadow DOM, so they do not directly leak into the rest of the page.

Next step

  • If you want to write a component API, see "Components".
  • If you want to write template logic, see "Template Syntax" and "Directives".
  • To access project diagnosis, see "Ecology/Vite Plug-in".