Create Your First App
npm create jen-js@latest my-app
cd my-app
npm install
npm run dev
Open http://localhost:3000 in your browser.
Creating Pages
Pages are files in the site/ directory. Each file becomes a route.
Simple Page
Create site/(hello).tsx:
export default function HelloPage() {
return (
<html>
<head>
<title>Hello</title>
</head>
<body>
<h1>Hello World</h1>
<p>This page is at /hello</p>
</body>
</html>
);
}
Visit http://localhost:3000/hello
Page with Data
Pages can load data using a loader function:
import type { LoaderContext } from '@src/core/routes';
interface PageData {
title: string;
message: string;
}
export async function loader(ctx: LoaderContext): Promise<PageData> {
return {
title: 'About Us',
message: 'Welcome to our site!'
};
}
export default function About({ data }: { data: PageData }) {
return (
<html>
<head>
<title>{data.title}</title>
</head>
<body>
<h1>{data.title}</h1>
<p>{data.message}</p>
</body>
</html>
);
}
Dynamic Routes
Create site/posts/($id).tsx:
import type { LoaderContext } from '@src/core/routes';
export async function loader(ctx: LoaderContext) {
const postId = ctx.params.id;
// Fetch post data
return {
id: postId,
title: `Post ${postId}`,
content: 'Post content here...'
};
}
export default function Post({ data }: any) {
return (
<html>
<head>
<title>{data.title}</title>
</head>
<body>
<h1>{data.title}</h1>
<p>{data.content}</p>
</body>
</html>
);
}
Access with URLs like /posts/1, /posts/hello, etc.
Interactive Pages
Use Preact hooks for interactivity:
import { useState } from 'preact/hooks';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<html>
<head>
<title>Counter</title>
</head>
<body>
<h1>Counter App</h1>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</body>
</html>
);
}
API Routes
Create site/api/(users).ts:
import type { IncomingMessage, ServerResponse } from 'node:http';
export async function handle(req: IncomingMessage, res: ServerResponse) {
if (req.method === 'GET') {
res.writeHead(200, { 'content-type': 'application/json' });
res.end(JSON.stringify({
users: [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
]
}));
}
}
Access with GET /api/users to get JSON response.
Building for Production
Static Site Generation
npm run build
Creates static HTML in dist/. Deploy to any CDN:
# Serve locally
npx serve dist
# Deploy to Netlify, Vercel, etc.
Server-Side Rendering
Keep dev server running:
npm run start
Or build and serve:
npm run build
npm run start
Project Structure
my-app/
├── site/
│ ├── (home).tsx # Homepage at /
│ ├── (about).tsx # About at /about
│ ├── posts/
│ │ └── ($id).tsx # Dynamic at /posts/:id
│ └── api/
│ └── (users).ts # API at /api/users
├── src/ # Optional: custom code
├── dist/ # Generated output
├── jen.config.ts # Framework config
├── tsconfig.json # TypeScript config
├── package.json
└── README.md
Common Tasks
Add a New Page
- Create file in
site/with naming pattern:(name).tsx - Export default component
- Optionally export
loaderfunction for data
Handle Forms
export async function handle(req: IncomingMessage, res: ServerResponse) {
if (req.method === 'POST') {
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
const data = JSON.parse(body);
// Process form data
res.writeHead(200);
res.end(JSON.stringify({ success: true }));
});
}
}
Add Styles
Create a CSS file or use inline styles:
const styles = `
body { font-family: sans-serif; }
h1 { color: blue; }
`;
export default function StyledPage() {
return (
<html>
<head>
<style>{styles}</style>
</head>
<body>
<h1>Styled Page</h1>
</body>
</html>
);
}
Use Components
Create reusable components in src/:
// src/components/header.tsx
export function Header() {
return <header><h1>My Site</h1></header>;
}
Use in pages:
import { Header } from '@src/components/header';
export default function Home() {
return (
<html>
<head><title>Home</title></head>
<body>
<Header />
<main>Home page</main>
</body>
</html>
);
}
Next Steps
- Learn about routing in detail
- Set up a database
- Add authentication
- Create API routes
- Deploy your app