Lists & Store Reference
This reference documents Coralite's application state management and high-performance list rendering utilities: the signal-backed Global Store and the built-in Lists Plugin.
1. Global Store #
The Global Store provides centralized, application-wide state management. It is declared in your project's configuration file and injected automatically into component and page execution contexts.
Declaration #
Declare initial store defaults in coralite.config.js using the store key:
import { defineConfig } from 'coralite'
export default defineConfig({
public: 'public',
output: 'dist',
pages: 'src/pages',
components: 'src/components',
store: {
user: { name: 'Alex', role: 'admin' },
theme: 'dark',
notifications: []
}
})
Component & Page Access #
Access the store directly via the store context key in component and page blocks (getters, client, etc.):
import { defineComponent } from 'coralite'
export default defineComponent({
getters: {
userName: ({ store }) => store.user.name,
isDark: ({ store }) => store.theme === 'dark'
},
client({ store, observe }) {
// Read and update global store state
console.log('Current user:', store.user)
// Update store state reactively
store.theme = 'light'
}
})
Reactivity: The Global Store is signal-backed. Reading a store property inside a getters declaration automatically registers subscriber tracking. When store.theme changes, any getter or component depending on store.theme re-evaluates and updates the DOM immediately.
Server vs. Client Behavior: On the server during build-time rendering, store is a read-only snapshot. Attempting to mutate store properties during SSR throws a CoraliteError. On the client runtime, store is fully reactive and mutable.
Store Methods #
The store object provides helper methods for subscription tracking, batched state mutations, and resetting state:
store.subscribe(key, callback)— Subscribes a callback to changes on a specific store key or across all store keys. Returns an unsubscribe function.// Subscribe to a specific key const unsubscribe = store.subscribe('theme', (newTheme, oldTheme) => { console.log(`Theme changed from ${oldTheme} to ${newTheme}`) }) // Unsubscribe when no longer needed unsubscribe() store.batch(fn)— Coalesces multiple synchronous store writes so subscriber notifications fire only once after the batch callback completes.store.batch(() => { store.theme = 'dark' store.notifications = [...store.notifications, { id: 1, text: 'Settings saved' }] }) store.reset()— Resets all store properties back to their initial defaults defined incoralite.config.js.// Resets store back to initial config defaults store.reset()
2. Lists Plugin (Basic Usage) #
The lists plugin is built-in and auto-registered in Coralite. It provides fine-grained DOM list rendering and virtualized collections without Virtual DOM diffing overhead.
Accessing the Plugin #
Destructure lists directly from the client() block context:
import { defineComponent } from 'coralite'
export default defineComponent({
client({ lists, refs, signal }) {
// 1. Create a keyed list store
const listStore = lists.createStore({
key: 'id',
initial: [
{ id: '1', title: 'Task One', done: false },
{ id: '2', title: 'Task Two', done: true }
]
})
// 2. Bind the list store to a DOM container
lists.bind({
container: refs.listContainer,
store: listStore,
signal,
create: (item) => {
const el = document.createElement('li')
el.className = 'task-item'
el.textContent = item.title
return el
},
update: (el, item) => {
el.classList.toggle('completed', item.done)
el.textContent = item.title
}
})
}
})
Keyed DOM Updates: lists.bind tracks items by unique key (specified via key: 'id'). Appending an item creates exactly one DOM element. Updating an item modifies only that item's DOM node. Swapping or reordering items re-positions existing DOM nodes without destroying component state or re-creating elements.
3. Lists Virtualization #
For large datasets, lists.bind supports built-in virtualization to keep rendering fast and DOM memory footprint low.
The virtual option supports three modes:
virtual: false(Default) — Renders all store items directly into the DOM container. Best for lists under 100 items.virtual: 'auto'— Uses CSScontent-visibility: autoto skip rendering off-screen elements. Ideal for lists containing 100 to 50,000 items.lists.bind({ container: refs.container, store: listStore, signal, virtual: 'auto', create: (item) => renderItem(item) }) virtual: 'window'— Uses anchor-based DOM windowing to recycle a fixed number of DOM nodes based on scroll position. Ideal for massive datasets (50,000+ items).lists.bind({ container: refs.container, store: listStore, signal, virtual: 'window', size: 48, // Fixed item height in pixels (or 'measure') overscan: 5, // Number of items to render outside visible viewport create: (item) => renderItem(item) })
Configuring size: Specify a numeric pixel value (e.g. size: 48) for fixed-height items, or pass size: 'measure' to dynamically compute item heights as elements enter the viewport.
4. Chat & Feeds (Anchor Features) #
The lists plugin provides built-in anchor options designed for real-time messaging, chat interfaces, and infinite scrolling feeds.
Chat Layouts (anchorTo & followOnAppend) #
For chat applications where messages append to the bottom and require automatically scrolling down when new messages arrive:
lists.bind({
container: refs.chatContainer,
store: chatStore,
signal,
anchorTo: 'end', // Stick scroll position relative to bottom
followOnAppend: true, // Automatically scroll to newest message when appended
create: (msg) => renderMessage(msg)
})
Infinite Scroll Feeds (onReachStart & onReachEnd) #
Attach scroll threshold callbacks to trigger pagination or dynamic data loading when users reach the top or bottom of a list:
lists.bind({
container: refs.feedContainer,
store: feedStore,
signal,
scrollEndThreshold: 150, // Trigger when within 150px of list boundary
onReachEnd: async () => {
// User scrolled near the bottom — load next page
const nextItems = await fetchNextPage()
feedStore.push(...nextItems)
},
onReachStart: async () => {
// User scrolled near the top — load previous history
const prevItems = await fetchHistory()
feedStore.unshift(...prevItems)
},
create: (post) => renderPost(post)
})
5. Layouts & Groups #
Customize list presentation and structure using built-in grid layouts and grouping options.
Layout Modes #
Set layout to standard display arrangements:
layout: 'linear'(Default) — Standard single-column vertical list.layout: 'grid'— Responsive multi-column CSS grid list. Configure withlanesorlaneWidth.layout: 'masonry'— Dynamic Pinterest-style masonry layout balancing columns by height.layout: 'table'— Tabular list format with structured column headers.
lists.bind({
container: refs.gridContainer,
store: galleryStore,
signal,
layout: 'grid',
lanes: 'auto',
minLaneWidth: 200, // Minimum column width in pixels
gap: 16, // Gap between items in pixels
create: (photo) => renderPhotoCard(photo)
})
Grouping & Sticky Headers #
Group items dynamically by key and render custom header elements:
lists.bind({
container: refs.groupedList,
store: transactionStore,
signal,
groupBy: (item) => item.date, // Group transactions by date string
stickyGroups: true, // Keep active group header sticky at top during scroll
createHeader: (groupKey, groupItems) => {
const header = document.createElement('div')
header.className = 'group-header'
header.textContent = `${groupKey} (${groupItems.length} transactions)`
return header
},
create: (tx) => renderTransactionRow(tx)
})