A DOM-first UI framework with JSX templates, standard component library and explicit lifecycle management.
npm install @duct-ui/core @duct-ui/componentsimport { createRef } from '@duct-ui/core'
import Button from '@duct-ui/components/button/button'
const buttonRef = createRef<ButtonLogic>()
function MyApp() {
return (
<Button
ref={buttonRef}
label="Click me"
class="btn btn-primary"
on:click={() => console.log('Clicked!')}
/>
)
}Access component methods via refs:
const buttonRef = createRef<ButtonLogic>()
// In render
<Button ref={buttonRef} label="Toggle" />
// Access methods
buttonRef.current?.setDisabled(true)Components follow explicit lifecycle phases:
- Render: JSX structure creation
- Load: Async data loading (optional)
- Bind: Event listeners and logic setup
- Release: Cleanup when component unmounts
import { createBlueprint, type BaseProps, type BaseComponentEvents } from '@duct-ui/core'
interface MyComponentProps {
label: string
disabled?: boolean
'on:click'?: (el: HTMLElement) => void
}
interface MyComponentEvents extends BaseComponentEvents {
click: (el: HTMLElement) => void
}
interface MyComponentLogic {
setDisabled: (disabled: boolean) => void
}
function render(props: BaseProps<MyComponentProps>) {
const { disabled = false, label, ...moreProps} = props;
return (
<button
class="btn"
disabled={disabled}
{...renderProps(moreProps)}
>
{label}
</button>
)
}
function bind(el: HTMLElement, eventEmitter, props): BindReturn<MyComponentLogic> {
const button = el as HTMLButtonElement
function handleClick() {
eventEmitter.emit('click', button)
}
button.addEventListener('click', handleClick)
return {
setDisabled: (disabled) => button.disabled = disabled,
release: () => button.removeEventListener('click', handleClick)
}
}
const MyComponent = createBlueprint<MyComponentProps, MyComponentEvents, MyComponentLogic>(
{ id: 'my-app/my-component' },
render,
{ bind }
)
export default MyComponent- @duct-ui/core: Core framework runtime and utilities
- @duct-ui/components: Pre-built component library
- @duct-ui/cli: Static site generation and build tools
- @duct-ui/demo: Interactive demos and documentation
Duct includes first-class support for static site generation with file-based routing:
npm install @duct-ui/cli --save-devBuild fast, SEO-friendly static sites with Duct components using file-based routing, dynamic routes, and Nunjucks layouts. Perfect for documentation sites, blogs, and marketing pages.
- Don't hide the DOM - Direct DOM access and manipulation
- Little magic, lots of logic - Explicit over implicit
- Easy packaging, simple reuse - Component composition and distribution