Components Reference

This reference details the Component API for authoring Web Components in Coralite. Every component is defined using standard ES module named exports (export function, export const) declared in a <script type="module"> block. The legacy export default defineComponent({ ... }) wrapper remains supported for backward compatibility.

1. Component Structure #

A Coralite component file combines template markup, JS logic, and component styles. The canonical file structure arranges <template>, <script type="module">, and <style> as top-level siblings:

html
Code copied!
  <template id="user-profile">
    <div class="card">
      <h2></h2>
      <p></p>
    </div>
  </template>
  
  &lt;script type="module">
    export const attributes = {
      userId: {
        type: String,
        required: true
      }
    }
  
    export async function server ({ state }) {
      return {
        name: 'Alex Developer',
        role: 'Core Contributor',
        id: state.userId
      }
    }
  &lt;/script&gt;
  
  <style>
    .card {
      padding: 1rem;
      border-radius: 8px;
    }
  </style>

In-Template Placement: Component parser normalization also permits placing <script type="module"> or <style> nested directly inside the <template> block. During compilation, the framework normalizes in-template tags and extracts them into the component's module definition.

2. attributes #

The attributes block defines the external HTML attributes accepted by the host element. Attributes are coerced and validated when parsed from host element markup.

Supported Types: Primitive types only (String, Number, Boolean). Passing Array or Object directly in attributes raises diagnostic CORALITE-E101. For complex dynamic data, pass data through server fetching or W3C context provider mechanisms.

javascript
Code copied!
  export const attributes = {
    // Shorthand syntax
    title: String,
    disabled: Boolean,
    count: Number,
  
    // Schema object syntax
    variant: {
      type: String,
      default: 'primary',
      values: ['primary', 'secondary', 'danger'],
      reflect: true
    },
    limit: {
      type: Number,
      default: 10,
      required: true,
      transform: (val) => Math.max(0, val),
      validate: (val) => val <= 100 || 'limit cannot exceed 100'
    }
  }

3. server() #

The server() handler executes on the server during build-time HTML rendering. It fetches external data, reads local files, or processes configuration required to compute the initial component state.

Context Object: Receives { state, route, app, signal, isServer, ...pluginContext }.

javascript
Code copied!
  export const attributes = {
    dataFile: { type: String, default: 'content.json' }
  }
  
  export async function server ({ state, isServer }) {
    if (!isServer) return {}
  
    const fs = await import('node:fs/promises')
    const raw = await fs.readFile(state.dataFile, 'utf-8')
    const data = JSON.parse(raw)
  
    return {
      items: data.items || [],
      lastUpdated: new Date().toISOString()
    }
  }

Returned properties are merged directly into the component's state object before template rendering.

4. getters #

The getters declaration provides synchronous derived state computations. Getter values update automatically whenever dependencies inside state change.

Context Object: Functions in getters receive a single unified context parameter ({ state, route, refs, slots, signal, isServer, ...pluginContext }). Parameters can be destructured or accepted as a whole context object (e.g. (context) => context.state.price). Naming the parameter state without destructuring (e.g. (state) => state.price) triggers CORALITE-E105 because the parameter holds the context object, not state directly.

javascript
Code copied!
  export const attributes = {
    price: Number,
    taxRate: { type: Number, default: 0.08 }
  }
  
  export const getters = {
    formattedPrice: ({ state }) => `$${state.price.toFixed(2)}`,
    total: ({ state }) => state.price * (1 + state.taxRate),
    badgeText: ({ state }) => `${state.price > 100 ? 'Premium' : 'Standard'}`
  }

5. style #

The style declaration defines dynamic CSS properties and CSS Custom Properties (e.g., --theme-color) that apply synchronously to host element scope.

Context Object: Style functions receive state directly: (state) => value.

Synchronous Execution: Style functions must execute synchronously. Returning a Promise or declaring an async style function triggers diagnostic CORALITE-E303.

javascript
Code copied!
  export const attributes = {
    color: { type: String, default: '#3b82f6' },
    padding: { type: Number, default: 16 }
  }
  
  export const style = {
    '--theme-primary': (state) => state.color,
    '--host-padding': (state) => `${state.padding}px`
  }

6. slots #

The slots declaration transforms or filters Light DOM children before slot distribution occurs in template rendering.

Context Object: Receives a unified context parameter ({ nodes, state, isServer, ...pluginContext }) => nodes or (context) => context.nodes.

javascript
Code copied!
  export const slots = {
    default: ({ nodes }) => {
      // Filter or sort incoming slot nodes
      return nodes.filter(node => node.nodeType === 1 && node.matches('.active'))
    }
  }

7. provide / consume #

Coralite implements the W3C Context Protocol via provide and consume declarations, allowing ancestor components to broadcast context values down the DOM tree to descendant components without prop drilling.

Context Keys: Use standard strings or keys returned by createContext('coralite:theme').

javascript
Code copied!
  // ThemeProvider.html
  import { createContext } from 'coralite'
  
  export const ThemeContext = createContext('coralite:theme')
  
  export const attributes = {
    theme: { type: String, default: 'dark' }
  }
  
  export const provide = {
    [ThemeContext]: 'dark'
  }
  
  // ThemeButton.html
  import { ThemeContext } from './ThemeProvider.html'
  
  export const consume = [ThemeContext]
  
  export function client ({ state }) {
    console.log('Active consumed theme:', state[ThemeContext])
  }

8. client() #

The client() handler executes in the browser environment once per component instance upon DOM mounting.

Context Object: Receives { state, route, refs, signal, emit, observe, slots, root, updateComplete, isServer, ...pluginContext }.

Critical Rule — Non-blocking updateComplete: Do not await updateComplete at the top of client(). The DOM is already settled when client() fires. Use await updateComplete only after mutating state if you need to measure rendered DOM element layout or dimensions afterwards.

Event Binding & Emitters: Bind event listeners with an AbortSignal provided via signal for automatic cleanup upon component unmount. Use emit('custom-event', detail) instead of calling dispatchEvent directly.

Lazy Dependency Stylesheets: Load third-party or local CSS files on demand inside client() via await import('pkg/styles.css', { media, nonce, crossorigin, referrerPolicy, signal, force }) (or standard Import Attributes { with: { type: 'css', ... }, signal, force }). Coralite bundles the stylesheet into a hashed asset under assets/css/, injects a global <link rel="stylesheet"> into <head>, and resolves to { default: link.sheet, link }.

javascript
Code copied!
  export const attributes = {
    initialCount: { type: Number, default: 0 }
  }
  
  export function client ({ state, refs, signal, emit, observe, updateComplete }) {
    state.count = state.initialCount
  
    // Reactive state observation
    observe('count', (newVal) => {
      emit('count-change', { value: newVal })
    })
  
    // DOM Event attachment with automatic unmount cleanup
    const btn = refs('incrementBtn')
    if (btn) {
      btn.addEventListener('click', async () => {
        state.count++
  
        // Measure DOM layout only after state mutation flush
        await updateComplete
        console.log('Updated button width:', btn.offsetWidth)
      }, { signal })
    }
  }

9. formControl (FACE) #

Exporting formControl = true (or legacy formAssociated: true) opts the custom component into Form-Associated Custom Elements (FACE). The framework attaches ElementInternals to host elements, integrating the component with standard HTML <form> elements.

javascript
Code copied!
  export const formControl = true
  
  export const attributes = {
    value: String,
    required: Boolean
  }
  
  export function client ({ state, refs, signal, setFormValue, setValidity, onFormReset }) {
    // Sync initial form value and validity
    setFormValue(state.value || '')
    if (state.required && !state.value) {
      setValidity({ valueMissing: true }, 'This field is required', refs('input'))
    } else {
      setValidity({})
    }
  
    // Input state synchronization
    const inputEl = refs('input')
    if (inputEl) {
      inputEl.addEventListener('input', (e) => {
        state.value = e.target.value
        setFormValue(state.value)
  
        if (state.required && !state.value) {
          setValidity({ valueMissing: true }, 'This field is required', inputEl)
        } else {
          setValidity({})
        }
      }, { signal })
    }
  
    // Handle form reset events
    onFormReset(() => {
      state.value = ''
      setFormValue('')
    })
  }

10. listStrategy #

Exporting listStrategy controls how reactive array mutations are reconciled in the DOM. It consolidates the former reconcile and key options.

Values: 'full' (default), 'minimal' (pattern-matched array patches), or an object { mode: 'minimal', key: (item) => item.id }.

javascript
Code copied!
  // Fast minimal pattern-matching with custom key extraction
  export const listStrategy = {
    mode: 'minimal',
    key: (item) => item.uuid
  }

11. onError #

The onError hook provides an SSR error boundary for component rendering during build time in production mode. When an exception occurs during server rendering of the component, onError intercepts the failure and returns a fallback HTML template or fallback state.

Return Shape: Returns an object { template, state } or string fallback HTML.

javascript
Code copied!
  export async function server () {
    throw new Error('External API request failed')
  }
  
  export function onError ({ error }) {
    console.error('SSR Component Render Failed:', error.message)
    return {
      template: '<div class="fallback-card">Content currently unavailable</div>',
      state: { errorOccurred: true }
    }
  }

12. Error Boundaries and Fault Isolation #

Coralite guarantees build isolation across independent page elements and dynamic routes:

13. No Defensive Guards #

Coralite guarantees determinism for all framework-provided context objects and plugin extensions passed to component handlers.

javascript
Code copied!
  // Access plugin context directly without defensive guards
  export function client ({ state, lists, router, isServer }) {
    if (isServer) return
  
    // Direct invocation without checking `if (!lists) return`
    const store = lists.createStore({ key: 'id' })
  }

Start Building with Coralite!

Use the scaffolding script to get jump started into your next project with Coralite

Copied commandline!