HTML Templating
Sapling provides two main methods for rendering HTML content: html
and raw
. These are based on the html
and raw
methods from Hono and we aim to be as compatible as possible with Hono's API to allow for easy migration between the two.
The html Template Literal
The html
template literal allows you to write HTML with dynamic content interpolation:
import { html } from "@hono/hono/html";
function Greeting({ name }: { name: string }) {
return html`
<!-- Tailwind CSS classes are used to style the component -->
<div class="flex flex-col items-center justify-center h-screen">
<h1 class="text-2xl font-bold">Hello, ${name}!</h1>
</div>
`;
}
Safety Features
The html
template automatically escapes interpolated values to prevent XSS attacks:
const userInput = '<script>alert("XSS")</script>';
html`<div>${userInput}</div>`; // Safely escaped
Nested Components
You can nest components and interpolate their results:
function Header() {
return html`<header>Site Header</header>`;
}
function Page() {
return html`
<div>
${Header()}
<main>Content</main>
</div>
`;
}
The raw Method
The raw
method is used when you need to insert pre-rendered HTML content without escaping:
import { html, raw } from "@sapling/sapling";
function Article({ content }: { content: string }) {
return html`
<article>
${raw(content)} <!-- Content is inserted as-is -->
</article>
`;
}
Use Cases for raw
- Rendering markdown content
- Inserting sanitized HTML from a CMS
- Including pre-rendered component output
import { renderMarkdown } from "@sapling/markdown";
async function MarkdownContent({ markdown }: { markdown: string }) {
const rendered = await renderMarkdown(markdown);
return html`
<div class="prose">
${raw(rendered)}
</div>
`;
}
Conditional Rendering
You can use standard JavaScript expressions within templates:
function ConditionalContent({ isLoggedIn }: { isLoggedIn: boolean }) {
return html`
<div>
${isLoggedIn
? html`<button>Logout</button>`
: html`<button>Login</button>`
}
</div>
`;
}
List Rendering
Render arrays of content using map:
function ItemList({ items }: { items: string[] }) {
return html`
<ul>
${items.map(item => html`
<li>${item}</li>
`)}
</ul>
`;
}
Best Practices
- Use html by Default: Always use the
html
template literal unless you specifically needraw
- Sanitize Raw Content: When using
raw
, ensure the content is from a trusted source or properly sanitized - Type Safety: Leverage TypeScript for component props
- Keep It Simple: Prefer small, focused components over complex templates
- Performance: Avoid unnecessary nesting of templates
Security Considerations
- The
html
template literal automatically escapes content to prevent XSS - Only use
raw
with trusted content - Always sanitize user-generated content before rendering
- Be cautious when rendering HTML from external sources