DBF Core: component engine (Web Components) + render + events + props
npm install dbf-coreDBFComponent base class (state, props, lifecycle)
defineComponent helper for ergonomic component definitions
html + render layer (template strings → shadow DOM)
bash
npm install dbf-core
`
DBF Core is framework‑agnostic and works anywhere you can register Custom Elements (plain HTML, React, Vue, etc.).
---
Quick start: define a component
`ts
import { defineComponent } from "dbf-core";
interface HelloProps {
name: string;
}
defineComponent("hello-name", {
props: { name: "string" } as const,
render({ props, html }) {
return html Hello, ${props.name}!
;
},
});
// HTML:
//
`
This registers a standard Custom Element that reads its props from the element’s attributes.
---
State and events
DBF Core lets you combine props + internal state + events in a small, React‑like way.
`ts
import { defineComponent, defineProps, type PropsFromSchema } from "dbf-core";
const counterProps = defineProps({
initial: "number",
} as const);
type CounterProps = PropsFromSchema;
interface CounterState {
count: number;
}
defineComponent("dbf-counter", {
props: counterProps,
state: () => ({ count: 0 }),
render({ state, props, html }) {
const value = state.count + (props.initial ?? 0);
return html
;
},
mount({ root, on, setState, host }) {
on(root, "click", "[data-action='inc']", () => {
setState({ count: host.state.count + 1 });
});
},
});
`
Key ideas:
- state is initialized once per instance via state: () => ({ ... }).
- render is called whenever state/props change.
- mount runs once after the component is attached; you typically use it for event delegation via on(root, "click", "[data-action='inc']", handler).
- setState accepts a partial object or a function: setState(prev => ({ count: prev.count + 1 })).
---
Per-component styles
DBF Core supports a styles field so you can inject styles into each component’s shadow root. With Vite (or similar bundlers) you can use ?inline to import CSS as a string.
`ts
import { defineComponent, defineProps, type PropsFromSchema } from "dbf-core";
import cardStyles from "./card.css?inline";
const cardProps = defineProps({
title: "string",
description: "string",
imageUrl: "string",
} as const);
type CardProps = PropsFromSchema;
defineComponent("dbf-card", {
props: cardProps,
styles: cardStyles,
render({ props, html }) {
return html
${props.description}
;
},
});
`
This keeps your styles scoped to the component via shadow DOM, and avoids leaking global CSS.
---
Typed props with
defineProps + PropsFromSchema
To avoid duplicating prop definitions in both runtime and TypeScript types, DBF Core exposes a small props helper:
`ts
import { defineProps, type PropsFromSchema } from "dbf-core";
const inputProps = defineProps({
placeholder: "string",
type: "string",
} as const);
type InputProps = PropsFromSchema;
`
- defineProps defines the runtime schema.
- PropsFromSchema infers the TypeScript type (placeholder: string; type: string;).
You can then plug inputProps directly into defineComponent’s options.
---
Relationship with the demo app
The apps/demo application in this repository:
- Registers several DBF Core components (cards, inputs, stats, etc.).
- Demonstrates page composition (a landing page built from custom elements).
- Integrates with dbf-router` to show client‑side navigation.