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:
<template id="user-profile">
<div class="card">
<h2></h2>
<p></p>
</div>
</template>
<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
}
}
</script>
<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.
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'
}
}
type: Primitive constructor (String,Number,Boolean).default: Default value when attribute is absent on host element.required: Set totrueto mandate attribute presence.values: Enumerated allowed values array.reflect: Set totrueto reflect host attribute back to DOM.transform: Pure function to sanitize or scale incoming attribute values.validate: Validator function returning boolean or error message string.
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 }.
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.
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.
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.
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').
// 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 }.
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.
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 }.
// 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.
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:
- Production Failure Isolation: If a component throws an error during server rendering in production mode, Coralite checks for an
onErrorboundary fallback. IfonErroris present, its fallback markup renders in place of the failed component without breaking the parent page build. - Development Diagnostic Mode: In development mode, component failures display diagnostic error overlays immediately in terminal output and browser logs, assisting instant troubleshooting.
- Build Isolation: Unhandled component errors without fallback handlers mark the affected page build output as failed while allowing sibling static pages to complete compilation without process crash.
13. No Defensive Guards #
Coralite guarantees determinism for all framework-provided context objects and plugin extensions passed to component handlers.
- Guaranteed Context Objects: Do not write defensive conditional guards checking framework context keys (for example, write
client({ lists, router }) { ... }directly without defensive code likeif (!lists) return). Context properties are guaranteed by the framework runtime. - Environment Branching: Always use the deterministic
isServerboolean parameter supplied in context objects to branch logic between build-time server rendering and browser runtime execution. Do not rely ontypeof window !== 'undefined'guards.
// 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' })
}