A lightweight, high-performance, and type-safe Server-Side Rendering (SSR) boilerplate. This project combines the robustness of Express with the raw speed of @kitajs/html (a JSX-to-string compiler), giving you React-like syntax without the React runtime overhead.
- Zero-Runtime JSX: Views compile directly to optimized strings. No Virtual DOM, no
react-dom/serveroverhead. - Type-Safe Views: Full TypeScript support for your components and props.
- Clean Architecture: Strict separation between Server logic (
.ts) and View logic (.tsx). - Developer Experience: Fast hot-reloading with
tsx. - Secure by Default: Automatic XSS escaping for all content.
-
Install dependencies:
npm install
-
Start the development server:
npm run dev
The server will start at
http://localhost:3000. -
Build for production:
npm run build
-
Run production server:
npm start
src/
├── lib/
│ └── render.ts # Helper to safely render components in Express
├── views/
│ ├── Home.tsx # Example Page Component
│ └── Layout.tsx # Example Layout Component
└── server.ts # Main Express application (Pure TypeScript)
Create a new .tsx file in src/views/. Components are just simple functions that return JSX.
// src/views/Profile.tsx
import Html from '@kitajs/html';
import { Layout } from './Layout';
interface ProfileProps {
username: string;
}
export function Profile({ username }: ProfileProps) {
return (
<Layout title={`Profile: ${username}`}>
<div class="profile-card">
<h1>Hello, {username}!</h1>
<a href="/">Back to Home</a>
</div>
</Layout>
);
}Import your component and use the render helper in src/server.ts. Do not call the component function directly; let render handle it.
// src/server.ts
import { render } from './lib/render';
import { Profile } from './views/Profile';
app.get('/user/:name', (req, res) => {
const { name } = req.params;
// Render safely with type-checking on props
render(res, Profile, { username: name });
});You can use standard CSS files served from a public folder, or include scoped styles directly in your components (though external CSS is recommended for larger apps).
// src/views/Components.tsx
export function Button({ label }: { label: string }) {
return (
<button style="background: blue; color: white;">
{label}
</button>
);
}IMPORTANT: Be mindful of XSS vulnerabilities, as @kitajs/html does NOT automatically escape child content.
While attributes are sanitized by default (e.g., <div title={userInput}>), content within tags (children) must be explicitly marked as safe if it contains user-controlled input.
-
Attributes are Safe by default:
<div attr={userInput}></div> // This will be escaped
-
Children are NOT Safe by default:
// ⚠️ DANGEROUS: This will NOT be escaped and WILL expose you to XSS if `userInput` contains malicious HTML. <div>{userInput}</div>
To prevent XSS in child content, always use the safe attribute or Html.escapeHtml/e:
// Safe approaches:
<div safe>{userInput}</div>
<div>{Html.escapeHtml(userInput)}</div>
<div>{e`This will be escaped: ${userInput}`}</div>Best Practice: Apply the safe attribute at the lowest possible level in the HTML tree where user-controlled input is rendered as a child. Our Layout.tsx now uses safe for props.title and props.children as a best practice for content coming into the layout.
MIT