Building Modern Web Apps with Deno Fresh
Learn how to build fast, server-rendered web applications using Fresh, the next-gen web framework for Deno
Building Modern Web Apps with Deno Fresh
Fresh is a next-generation web framework for Deno that embraces server-side rendering and minimal client-side JavaScript. If you've worked with frameworks like Next.js or Remix, Fresh will feel familiar while offering unique advantages.
What Makes Fresh Different?
Islands Architecture
Fresh uses the "islands architecture" pattern - only interactive components (islands) ship JavaScript to the client. Static content stays static, dramatically reducing bundle sizes.
// This is a static component - no JS sent to client
export default function Header() {
return (
<header>
<h1>My Site</h1>
</header>
);
}
// islands/Counter.tsx - Interactive island
import { useState } from "preact/hooks";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}
Zero Config
Fresh works out of the box with:
- TypeScript support
- Hot module reloading
- Server-side rendering
- File-based routing
Lightning Fast
- No build step required in development
- Instant page loads with SSR
- Minimal JavaScript payloads
- Native ESM support
Core Concepts
File-Based Routing
routes/
index.tsx → /
about.tsx → /about
blog/
index.tsx → /blog
[slug].tsx → /blog/:slug
api/
posts.ts → /api/posts
Route Handlers
Fresh routes export handlers for different HTTP methods:
import { Handlers, PageProps } from "fresh";
export const handler: Handlers = {
async GET(req, ctx) {
const posts = await fetchPosts();
return ctx.render({ posts });
},
};
export default function BlogPage({ data }: PageProps) {
return (
<div>
{data.posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
</article>
))}
</div>
);
}
Middleware
Add middleware for auth, logging, etc.:
// routes/_middleware.ts
import { MiddlewareHandler } from "fresh";
export const handler: MiddlewareHandler[] = [
async (req, ctx) => {
// Run before route handler
const start = Date.now();
const resp = await ctx.next();
const ms = Date.now() - start;
resp.headers.set("X-Response-Time", `${ms}ms`);
return resp;
},
];
Building a Real App
Here's a simple blog with Fresh:
// routes/blog/[slug].tsx
import { Handlers, PageProps } from "fresh";
import { extract } from "@std/front-matter";
interface Post {
slug: string;
title: string;
content: string;
}
export const handler: Handlers<Post> = {
async GET(_req, ctx) {
const { slug } = ctx.params;
const text = await Deno.readTextFile(`./content/${slug}.md`);
const { attrs, body } = extract(text);
return ctx.render({
slug,
title: attrs.title,
content: body,
});
},
};
export default function BlogPost({ data }: PageProps<Post>) {
return (
<article>
<h1>{data.title}</h1>
<div dangerouslySetInnerHTML={{ __html: data.content }} />
</article>
);
}
Performance Benefits
Fresh's approach yields impressive results:
- Lighthouse scores: 100 across the board
- Time to Interactive: < 100ms
- Bundle size: Only islands are shipped as JS
- SEO: Perfect - everything is server-rendered
Conclusion
Fresh represents a paradigm shift in web development - away from heavy client-side frameworks toward lean, server-rendered applications. With its islands architecture and zero-config approach, Fresh makes it easy to build fast, modern web apps.
Try Fresh for your next project and experience the difference!
Jordan Patel
Webentwickler & Technologie-Enthusiast