fresh

Full Reference: See advanced.md for Shared Signals, Middleware (Auth, CORS), Plugins, Error Handling, and Production Readiness.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "fresh" with this command: npx skills add claude-dev-suite/claude-dev-suite/claude-dev-suite-claude-dev-suite-fresh

Fresh Core Knowledge

Full Reference: See advanced.md for Shared Signals, Middleware (Auth, CORS), Plugins, Error Handling, and Production Readiness.

Basic Setup

Create new Fresh project

deno run -A -r https://fresh.deno.dev my-project

Project structure

my-project/ ├── deno.json ├── dev.ts ├── main.ts ├── fresh.gen.ts # Auto-generated manifest ├── routes/ # File-based routing ├── islands/ # Interactive components ├── components/ # Static components └── static/ # Static assets

Configuration

// deno.json { "tasks": { "start": "deno run -A --watch=static/,routes/ dev.ts", "build": "deno run -A dev.ts build", "preview": "deno run -A main.ts" }, "imports": { "$fresh/": "https://deno.land/x/fresh@1.6.8/", "preact": "https://esm.sh/preact@10.19.6", "@preact/signals": "https://esm.sh/*@preact/signals@1.2.2" }, "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "preact" } }

File-Based Routing

// routes/index.tsx export default function Home() { return <h1>Welcome to Fresh</h1>; }

// routes/users/[id].tsx - Dynamic route import { PageProps } from "$fresh/server.ts";

export default function UserPage(props: PageProps) { const { id } = props.params; return <h1>User: {id}</h1>; }

// routes/blog/[...slug].tsx - Catch-all export default function BlogPost(props: PageProps) { const { slug } = props.params; return <h1>Post: {slug}</h1>; }

// routes/(marketing)/pricing.tsx - Route groups // URL: /pricing (group name not in URL)

Handlers

// routes/api/users.ts import { Handlers } from "$fresh/server.ts";

export const handler: Handlers = { GET(_req, _ctx) { const users = [{ id: 1, name: "Alice" }]; return new Response(JSON.stringify(users), { headers: { "Content-Type": "application/json" }, }); },

async POST(req, ctx) { const body = await req.json(); // Process... return new Response(JSON.stringify(body), { status: 201 }); }, };

Handler with Page

// routes/greet/[name].tsx import { Handlers, PageProps } from "$fresh/server.ts";

interface Data { greeting: string; }

export const handler: Handlers<Data> = { GET(_req, ctx) { return ctx.render({ greeting: Hello, ${ctx.params.name}! }); }, };

export default function GreetPage({ data }: PageProps<Data>) { return <h1>{data.greeting}</h1>; }

Islands Architecture

// islands/Counter.tsx import { useSignal } from "@preact/signals";

export default function Counter() { const count = useSignal(0);

return ( <div> <p>Count: {count.value}</p> <button onClick={() => count.value++}>Increment</button> </div> ); }

// routes/index.tsx - Using islands import Counter from "../islands/Counter.tsx";

export default function Home() { return ( <div> <h1>My App</h1> {/* This component is hydrated on the client */} <Counter /> </div> ); }

Islands with Props

// islands/Greeting.tsx import { useSignal } from "@preact/signals";

interface GreetingProps { initialName: string; }

export default function Greeting({ initialName }: GreetingProps) { const name = useSignal(initialName);

return ( <div> <input type="text" value={name.value} onInput={(e) => name.value = (e.target as HTMLInputElement).value} /> <p>Hello, {name.value}!</p> </div> ); }

Signals (State Management)

// islands/TodoList.tsx import { useSignal, useComputed } from "@preact/signals";

export default function TodoList() { const todos = useSignal<{ id: number; text: string; done: boolean }[]>([]); const newTodo = useSignal("");

const remaining = useComputed(() => todos.value.filter((t) => !t.done).length );

const addTodo = () => { if (newTodo.value.trim()) { todos.value = [...todos.value, { id: Date.now(), text: newTodo.value, done: false, }]; newTodo.value = ""; } };

return ( <div> <input value={newTodo.value} onInput={(e) => newTodo.value = (e.target as HTMLInputElement).value} onKeyDown={(e) => e.key === "Enter" && addTodo()} /> <button onClick={addTodo}>Add</button> <p>{remaining.value} items remaining</p> </div> ); }

Static Components

// components/Header.tsx import { JSX } from "preact";

interface HeaderProps { title: string; children?: JSX.Element; }

export function Header({ title, children }: HeaderProps) { return ( <header> <h1>{title}</h1> <nav>{children}</nav> </header> ); }

Forms

// routes/contact.tsx import { Handlers, PageProps } from "$fresh/server.ts";

interface PageData { error?: string; success?: boolean; }

export const handler: Handlers<PageData> = { GET(_req, ctx) { return ctx.render({}); },

async POST(req, ctx) { const form = await req.formData(); const name = form.get("name")?.toString(); const email = form.get("email")?.toString();

if (!name || !email) {
  return ctx.render({ error: "All fields required" });
}

return ctx.render({ success: true });

}, };

export default function ContactPage({ data }: PageProps<PageData>) { return ( <div> {data.error && <p style={{ color: "red" }}>{data.error}</p>} {data.success && <p style={{ color: "green" }}>Sent!</p>}

  &#x3C;form method="POST">
    &#x3C;input type="text" name="name" required />
    &#x3C;input type="email" name="email" required />
    &#x3C;button type="submit">Send&#x3C;/button>
  &#x3C;/form>
&#x3C;/div>

); }

Basic Middleware

// routes/_middleware.ts import { FreshContext } from "$fresh/server.ts";

export async function handler(req: Request, ctx: FreshContext) { const start = performance.now(); const resp = await ctx.next(); const duration = performance.now() - start; resp.headers.set("X-Response-Time", ${duration.toFixed(2)}ms); return resp; }

Checklist

  • Islands only for interactive components

  • Static components for server-rendered content

  • Middleware for cross-cutting concerns

  • Error pages (_404.tsx, _500.tsx)

  • Health/readiness endpoints

  • Environment variables via Deno.env

  • Form validation on both client and server

When NOT to Use This Skill

  • Node.js Projects: Use Next.js, Remix

  • Pure API Servers: Use Oak for Deno API-only apps

  • Edge Runtimes: Use Hono for Cloudflare Workers

  • Static Sites: Use Astro or other SSG tools

Anti-Patterns

Anti-Pattern Why It's Bad Correct Approach

Using islands for static content Unnecessary hydration Use static components

Heavy computation in islands Slow client-side Compute on server

Fetching data in islands Waterfalls Fetch in handlers

Hardcoding env variables Not portable Use Deno.env.get()

Quick Troubleshooting

Issue Solution

Island not hydrating Move component to islands/ folder

Props not passing Ensure props are JSON-serializable

Handler not executing Ensure handler is exported

404 for dynamic route Use [param].tsx naming

Signals not reactive Access with .value

Reference Documentation

  • Routing

  • Handlers

  • Islands

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

cron-scheduling

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

token-optimization

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

webrtc

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

react-19

No summary provided by upstream source.

Repository SourceNeeds Review