Advanced Guide: Authoring Plugins
Plugins are the primary way to extend Coralite's functionality. The framework's architecture is designed to be highly extensible via the createPlugin function.
With plugins, you can hook deep into the build lifecycle, register custom components globally, bundle external scripts, and inject custom helper functions directly into the frontend.
The Conceptual Flow #
Authoring a plugin involves understanding how Coralite synthesizes server-side rendering and client-side hydration. When you register a plugin, Coralite integrates it through the following flow:
- Registration & Bootstrapping: Coralite loads your plugin via
createPlugin, reading itsclient.configand bundling any dependencies defined inclient.imports. - Server Execution: As the generator processes pages and encounters dynamic components, it fires your plugin's lifecycle hooks (e.g.,
onPageSet,onBeforePageRender). It also executesclient.setup()asynchronously to fetch any required data. - Data Binding & Injection: Coralite processes the component's tokens and slots. Crucially, it merges the data returned from your
client.setup()into the component'svaluesobject. - Client Hydration: Finally, Coralite serializes your component's
client.scriptand your plugin's customhelpers. When the browser loads the page, the bundled modules are provided securely via thehelperscontext, enabling dynamic interactions.
Server-Side Lifecycle Hooks #
Plugins can hook into almost every phase of Coralite's build lifecycle to perform actions when files are processed or rendering occurs.
- Page Lifecycle:
onPageSet(data): Called when a page is created. Ideal for extracting metadata (like the built-in metadata plugin).onPageUpdate(data): Called when a page is updated.onPageDelete(path): Called when a page is deleted.
- Component Lifecycle:
onComponentSet(data): Called when a component is registered.onComponentUpdate(data): Called when a component's source file is updated.onComponentDelete(path): Called when a component is deleted.
- Render Lifecycle:
onBeforePageRender(data): Called right before a specific page's HTML is compiled. Useful for modifying the raw document data before rendering. Note: Mutate the passed context objects directly by reference rather than returning new objects to avoid merge conflicts with other plugins.onAfterPageRender(data): Called immediately after a specific page's HTML is compiled.
- Build Lifecycle:
onBeforeBuild(): Called before the entire static generation process starts.onAfterBuild(): Called when the entire build process finishes.
Global Component Registration #
Plugins can distribute their own HTML components. By providing an array of file paths to the components property in your plugin configuration, Coralite automatically reads, parses, and registers these files as global components during initialization.
This means users of your plugin can instantly use your custom tags (e.g., <my-awesome-slider>) without having to copy the HTML files into their own project's src/components/ directory.
Injecting Client Helpers #
The most powerful feature of Coralite plugins is the ability to provide reusable functionality directly to component scripts using client.helpers.
Helpers are injected into the context.helpers object available in every client.script. However, because Coralite is a static site generator, there is a strict boundary between server code and client code.
The Factory Pattern #
Helpers must be structured using a two-phase currying system that executes server-side. The outermost function receives the global context, and it must return a second function that receives the local Web Component instance context. This second function then returns the actual callable utility function used by the user's script.
To use client-only APIs (like window, document, or the injected AbortSignal), the injected helper function must encapsulate that logic within the final returned function.
// Server-side definition in createPlugin
client: {
helpers: {
// Phase 1: Runs on the SERVER and receives global context
trackEvent: (globalContext) => {
const apiKey = globalContext.config.apiKey;
// Phase 2: Receives the local Web Component instance context
return (localContext) => {
const { signal } = localContext;
// Phase 3: The actual function accessible on the CLIENT
return (buttonElement, eventName) => {
// Now we can safely use browser APIs and component lifecycle signals
buttonElement.addEventListener('click', () => {
window.navigator.sendBeacon('https://api.analytics.com', JSON.stringify({
key: apiKey,
event: eventName
}));
}, { signal });
};
}
}
}
}
Managing Dependencies with Imports #
If your plugin relies on an external library (like a charting library or an analytics SDK), you define it in client.imports.
Coralite handles bundling these external libraries—whether they are remote ESM URLs, local JS/JSON files, or sibling components. The imported modules are rigorously validated and then surfaced in the browser post-load, making them accessible via the context.imports object passed to your helper factories.
Ready to build your plugin? Consult the strict API signatures and configuration options in the createPlugin API Reference.