Skip to content

DOM Rendering

This guide covers DOM rendering with @rasenjs/dom.

Overview

The DOM adapter provides components that render to HTMLElement hosts:

typescript
import { div, button, span, mount } from '@rasenjs/dom'

Basic Usage

typescript
import { setReactiveRuntime } from '@rasenjs/core'
import { createVueRuntime } from '@rasenjs/reactive-vue'
import { div, button, span, mount } from '@rasenjs/dom'
import { ref } from 'vue'

setReactiveRuntime(createVueRuntime())

const count = ref(0)

const Counter = () => div({
  children: [
    span({ textContent: () => `Count: ${count.value}` }),
    button({ textContent: '+', on: { click: () => count.value++ } })
  ]
})

mount(Counter(), document.getElementById('app'))

Element Components

All standard HTML elements are available:

typescript
import {
  div, span, p, a, img, br, hr,
  h1, h2, h3, h4, h5, h6,
  button, input, textarea, select, option, label, form,
  ul, ol, li, dl, dt, dd,
  table, thead, tbody, tr, th, td,
  header, footer, main, nav, section, article, aside,
  canvas, svg
} from '@rasenjs/dom'

Props

Basic Props

typescript
div({
  id: 'my-div',
  className: 'container',
  textContent: 'Hello World'
})

Reactive Props

All props can be reactive:

typescript
const isActive = ref(false)

div({
  className: () => isActive.value ? 'active' : 'inactive',
  textContent: computed(() => isActive.value ? 'ON' : 'OFF')
})

Styles

typescript
// Static style
div({ style: { color: 'red', fontSize: '16px' } })

// Reactive style
div({
  style: () => ({
    backgroundColor: isHovered.value ? 'blue' : 'gray',
    transform: `scale(${scale.value})`
  })
})

Attributes

typescript
div({
  attrs: {
    'data-id': '123',
    'aria-label': 'Description',
    disabled: isDisabled.value
  }
})

Event Handlers

typescript
button({
  on: {
    click: (e) => console.log('Clicked!', e),
    mouseenter: () => isHovered.value = true,
    mouseleave: () => isHovered.value = false
  }
})

Children

typescript
div({
  children: [
    h1({ textContent: 'Title' }),
    p({ textContent: 'Paragraph 1' }),
    p({ textContent: 'Paragraph 2' })
  ]
})

Form Inputs

Text Input

typescript
const text = ref('')

input({
  value: text,
  attrs: { type: 'text', placeholder: 'Enter text...' },
  on: {
    input: (e) => text.value = (e.target as HTMLInputElement).value
  }
})

Checkbox

typescript
const checked = ref(false)

input({
  attrs: { type: 'checkbox', checked: checked.value },
  on: {
    change: (e) => checked.value = (e.target as HTMLInputElement).checked
  }
})

Select

typescript
const selected = ref('option1')

select({
  value: selected,
  on: {
    change: (e) => selected.value = (e.target as HTMLSelectElement).value
  },
  children: [
    option({ attrs: { value: 'option1' }, textContent: 'Option 1' }),
    option({ attrs: { value: 'option2' }, textContent: 'Option 2' })
  ]
})

Generic Element

For non-standard elements or web components:

typescript
import { element } from '@rasenjs/dom'

element({
  tag: 'custom-element',
  attrs: { 'custom-attr': 'value' },
  children: [...]
})

Mount/Unmount

typescript
// Mount returns unmount function
const unmount = mount(component, container)

// Later, to unmount:
unmount?.()

Best Practices

1. Use Getters for Computed Strings

typescript
// ✅ Good
span({ textContent: () => `Count: ${count.value}` })

// ❌ Avoid (creates new computed every render)
span({ textContent: computed(() => `Count: ${count.value}`) })

2. Extract Event Handlers

typescript
// ✅ Good
const handleClick = () => count.value++

button({ on: { click: handleClick } })

3. Batch Style Updates

typescript
// ✅ Good - single reactive object
div({
  style: () => ({
    width: `${width.value}px`,
    height: `${height.value}px`,
    opacity: opacity.value
  })
})

Released under the MIT License.