Duct UI

What is Duct?

Introduction to the Duct UI Framework

Duct is a modern UI framework that provides a clear separation between view code (templates) and logic code (companions). It embraces simplicity and maintainability through a well-defined component architecture.

Core Architecture

Blueprint Pattern

Every Duct component follows a blueprint pattern with distinct phases:

Render

Pure JSX templates that define the component's structure and initial state

Load

Optional async data fetching phase that runs before binding

Bind

Event handling, DOM manipulation, and component logic initialization

Release

Cleanup phase that removes listeners and frees resources

Separation of Concerns

Duct enforces a clean separation between different aspects of component development:

ConcernLocationPurpose
PresentationRender FunctionDefine HTML structure and styling
Data LoadingLoad FunctionFetch data asynchronously before binding
BehaviorBind FunctionHandle interactions and state changes
CleanupRelease FunctionRemove listeners and prevent memory leaks

Key Features

Type-Safe Event System

Duct provides a strongly typed event system built on TypeScript:

// Define component events with full type safety
export interface ButtonEvents extends BaseComponentEvents {
  click: (el: HTMLElement) => void
  stateChange: (el: HTMLElement, newState: string) => void
}

// Use events in templates with clear syntax
<Button
  label="Click me"
  on:click={handleButtonClick}
  on:stateChange={handleStateChange}
/>

Direct DOM Manipulation

Duct components have direct access to DOM elements, allowing for efficient updates without virtual DOM overhead:

function bind(el: HTMLElement, eventEmitter: EventEmitter<Events>) {
  const button = el.querySelector('button')

  // Direct DOM updates are fast and explicit
  function updateState(newState: string) {
    button.className = `btn btn-${newState}`
    button.dataset.state = newState
  }
}

Precompiled Templates

Templates are compiled at build time for optimal performance, with no runtime template parsing overhead.

Component Logic Access

Components expose their logic for programmatic control using refs:

// Recommended: Use refs for synchronous access
import { Button } from '@duct-ui/components'
const buttonRef = createRef()

<Button ref={buttonRef} label="Test" />

// Access component logic immediately
buttonRef.current?.setDisabled(true)
buttonRef.current?.updateLabel('New Text')

Lifecycle Flow

1. Render

Generate initial HTML structure with JSX templates

2. Load

Fetch data asynchronously (optional) - perfect for API calls

3. Bind

Attach event listeners, initialize component logic, and update DOM with loaded data

4. Release

Clean up event listeners and resources when component is removed

Philosophy: Duct prioritizes simplicity, maintainability, and developer happiness through clear patterns and explicit behavior over magical abstractions.