Core Concepts: Dynamic Components
While Static Components are great for simple layouts, modern web development often requires more power. When your component needs data processing, dynamic attribute binding, custom slot processing, or client-side interactivity, you must create a Dynamic Component.
Dynamic components are powered by Coralite's core built-in plugin: defineComponent.
Upgrading a Component #
To turn a static component into a dynamic one, you simply append a <script type="module"> tag below your <template> and export defineComponent.
<template id="my-dynamic-component">
<div>...</div>
</template>
<script type="module">
import { defineComponent } from 'coralite'
export default defineComponent({
// Configuration goes here
})
</script>
Tokens: Build-time Evaluation #
In a static component, {{ data }} simply maps to an HTML attribute. In a dynamic component, you can define Tokens. Tokens evaluate data at build time (during server-side rendering).
Tokens can be static strings or computed functions. Computed functions receive a values object, which contains all the attributes passed to the component, plus page metadata.
<template id="greeting-card">
<div class="card">
<h2>{{ formattedGreeting }}</h2>
<p>Status: {{ status }}</p>
</div>
</template>
<script type="module">
import { defineComponent } from 'coralite'
export default defineComponent({
tokens: {
// Static string token
status: 'Active',
// Computed function token receiving 'name' attribute
formattedGreeting: ({ name }) => {
const cleanName = name ? name.trim().toUpperCase() : 'GUEST'
return `Welcome, ${cleanName}!`
}
}
})
</script>
When used as <greeting-card name=" alice "></greeting-card>, the output will be "Welcome, ALICE!".
Slots: Server-Side Processing #
While static components use the standard <slot> tag blindly, defineComponent allows you to intercept and process slot content on the server before it's rendered.
Slot functions receive an array of parsed HTML nodes (slotNodes) and the current values. You can mutate tags, replace them, or map over them conditionally.
<template id="smart-list">
<ul class="list">
{{ items }}
</ul>
</template>
<script type="module">
import { defineComponent } from 'coralite'
export default defineComponent({
slots: {
// The slot name matches the token {{ items }} in the template
items: (slotNodes, values) => {
// Transform the content passed into the slot
return slotNodes.map(node => {
// If the user passed <li> elements, automatically add a class
if (node.type === 'tag' && node.name === 'li') {
node.attributes.class = 'list-item-styled'
}
return node
})
}
}
})
</script>
Server-Side Setup #
If your component needs to fetch data from an API or read files before rendering, use the client.setup function. It effectively replaces Top-Level Await (TLA) in component modules.
The object returned by setup is seamlessly merged into the component's values object, making that data available to tokens and client scripts.
<script type="module">
import { defineComponent } from 'coralite'
export default defineComponent({
client: {
// This runs ON THE SERVER during the build process
setup: async (values) => {
const response = await fetch('https://api.example.com/data')
const data = await response.json()
// Merge this data into the component's values
return {
fetchedData: data.message
}
}
},
tokens: {
// Now we can use the fetched data in our template
apiMessage: ({ fetchedData }) => fetchedData
}
})
</script>
Client-Side Interactivity (The Browser Script) #
The client.script function is what makes Coralite components interactive. This function is serialized and bundled to run in the user's browser after the page loads.
The script receives a context object (CoraliteScriptContent) containing things like id, values, and helpers.
<template id="toggle-box">
<div ref="box" class="box closed">
{{ content }}
</div>
<button type="button" ref="toggleBtn">Toggle</button>
</template>
<script type="module">
import { defineComponent } from 'coralite'
export default defineComponent({
client: {
// This runs IN THE BROWSER
script: (context) => {
// Extract the refs helper
const { helpers } = context
const refs = helpers.refs
// Get DOM elements safely
const box = refs('box')
const btn = refs('toggleBtn')
btn.addEventListener('click', () => {
box.classList.toggle('closed')
box.classList.toggle('open')
})
}
}
})
</script>
Notice the use of ref="box" and helpers.refs('box')? This is Coralite's safe way to query the DOM. Learn more in the Managing the DOM (Refs) guide.
For strict API type definitions, arguments, and return types, see the defineComponent API Reference.