Site Modules
Site modules are a powerful feature in Sapling that allows you to package and distribute entire websites as reusable modules. This approach enables a clear separation between the site's structure/functionality and its content, making it easier to create templated solutions that others can easily customize.
Another great use case for site modules is for web design agencies to create templates that clients can easily customize but the agency can maintain the underlying structure and components under one single module and codebase.
How Site Modules Work
A site module typically contains all the components, layouts, and routing logic of your website, while allowing the end user to simply provide the content. This is particularly useful for creating reusable website templates, documentation sites, a reusable APIs, or any other type of website where you want to maintain a consistent structure but allow for custom content.
Creating a Module
To create a site module, you'll need to:
- Build your site structure with Sapling
- Export the necessary configuration functions
- Publish your module to a registry (like JSR)
Here's a basic example of how to structure a site module:
import { Sapling, Layout, html, type HtmlContent } from "@sapling/sapling";
export const site: Sapling = new Sapling();
// Define the content types for the site module
export interface LandingLayoutContent {
siteTitle: string;
hero: {
title: string;
subtitle: string;
}
}
// Add a configuration option for the content.
// This could also be something like the configuration for a CMS.
// That way the end user only has to provide an API key.
let siteContent: LandingLayoutContent;
export function configureSite(content: LandingLayoutContent) {
siteContent = content;
}
site.get("/", async (c) => {
if (!siteContent) {
throw new Error("Site content not configured. Call configureSite() before starting the server.");
}
return c.html(
await Layout({
unoConfig: config,
head: html`
<title>${siteContent.siteTitle}</title>
`,
bodyClass: "text-gray-900 font-sans min-h-screen @dark:bg-gray-900 @dark:text-white",
children: html`
<div class="relative z-10">
<main>
<h1>${siteContent.hero.title}</h1>
<p>${siteContent.hero.subtitle}</p>
</main>
`,
}));
});
Publishing a Module
Technically, you could publish your site module to any registry that supports ESM modules. However, we recommend using JSR because it's free, easy, and has a much better developer experience than NPM.
Follow the documentation from JSR to publish your site module. However, it should be pretty much as simple as running deno publish
from the root of your Sapling site.
Using a Module
And then the end user can import and configure the site module like this:
import { site, configureSite } from "jsr:@sapling/site-0";
const content = {
siteTitle: "My Site",
hero: {
title: "Hello, world!",
subtitle: "Welcome to my site.",
},
};
// Configure the site with the content
configureSite(content);
Deno.serve(site.fetch);
Caveats
The only caveat to keep in mind is any static files you are serving from a static
directory on your Sapling site will not be included in the site module. There are three ways to work around this:
- Host your files elsewhere such as in a CMS, CDN, etc. And then just use the URLs to the files directly in your site module. (recommended)
- Expect your end users to provide their own URLs for static files. This would be if you don't want to incur the cost of hosting the files yourself.
- Provide the source files to your end users for them to host themselves which is obviously the most cumbersome but still works.
This is a null point if your site module is primarily designed to hook into a specific CMS or other system because the CMS is likely handling all of the file hosting.
Conclusion
And yes, that's really all there is to it. Your site module can be as complex or as simple as you'd like. And because of TypeScript, it's fully typed and you'll have autocompletion for the content you provide.