1---2name: deno-frontend3description: Use when working with Fresh framework, creating routes or handlers in Fresh, building web UIs with Preact, or adding Tailwind CSS in Deno. Covers Fresh 2.x project structure, route handlers, islands, createDefine, PageProps, context patterns, and Fresh 1.x to 2.x migration. Essential for any Fresh-related question.4license: MIT5metadata:6 author: denoland7 version: "2.4"8---910# Deno Frontend Development1112## Overview1314This skill covers frontend development in Deno using Fresh 2.x (Deno's web framework), Preact (a lightweight React alternative), and Tailwind CSS. Fresh uses "island architecture" where pages render on the server and only interactive parts ship JavaScript to the browser.1516## When to Use This Skill1718- Creating a new Fresh web application19- Building interactive UI components (islands)20- Adding server-rendered pages and routes21- Integrating Tailwind CSS for styling22- Choosing between islands (client-side) vs components (server-only)23- Working with Preact hooks and signals2425Apply these practices when building web applications in Deno.2627## Scope Boundaries2829This skill applies **only** to Fresh/Deno frontend questions. Follow these rules:3031- If the user asks about **Next.js, React Native, Vue, Svelte, Angular, or any other framework**, answer using that framework directly. Do not suggest Fresh alternatives.32- Do not include Fresh imports, `useSignal`, island patterns, or Fresh project structure in responses about other frameworks.33- Only recommend Fresh when the user explicitly asks about Fresh or is working in a Fresh/Deno project.3435## CRITICAL: Never Show Deprecated Syntax3637When helping users migrate from Fresh 1.x, describe old patterns generically and ONLY show correct Fresh 2.x code. Never write out old dollar-sign import paths or deprecated syntax, even in "before/after" comparisons.3839- Say "Replace the old dollar-sign import paths with stable Fresh 2.x imports" — then show only the correct `from "fresh"` approach40- Do NOT write `❌ Old: import { App } from "$fresh/server.ts"` — this is never acceptable, even as a negative example41- The strings `_404.tsx` and `_500.tsx` must never appear in your response, even when comparing Fresh 2.x to 1.x. Say "the old separate error pages" instead.4243Only demonstrate Fresh 2.x patterns.4445## CRITICAL: Fresh 2.x vs 1.x4647**Always use Fresh 2.x patterns.** Fresh 1.x is deprecated. Key differences:4849- Fresh 2.x uses `import { App } from "fresh"` — the old dollar-sign import paths are deprecated50- Fresh 2.x has no manifest file — the old auto-generated manifest is no longer needed51- Fresh 2.x uses `vite.config.ts` for dev — the old `dev.ts` entry point is gone52- Fresh 2.x configures via `new App()` — the old config file is no longer used53- Fresh 2.x handlers take a single `(ctx)` parameter — the old two-parameter signature is deprecated54- Fresh 2.x uses a unified `_error.tsx` — the old separate error pages are replaced5556**Always use Fresh 2.x stable imports:**57```typescript58// ✅ CORRECT - Fresh 2.x stable59import { App, staticFiles } from "fresh";60import { define } from "./utils/state.ts"; // Project-local define helpers61```6263## Fresh Framework6465Reference: https://fresh.deno.dev/docs6667Fresh is Deno's web framework. It uses **island architecture** - pages are rendered on the server, and only interactive parts ("islands") get JavaScript on the client.6869### Creating a Fresh Project7071```bash72deno run -Ar jsr:@fresh/init73cd my-project74deno task dev # Runs at http://127.0.0.1:5173/75```7677### Project Structure (Fresh 2.x)7879```80my-project/81├── deno.json # Config, dependencies, and tasks82├── main.ts # Server entry point83├── client.ts # Client entry point (CSS imports)84├── vite.config.ts # Vite configuration85├── routes/ # Pages and API routes86│ ├── _app.tsx # App layout wrapper (outer HTML)87│ ├── _layout.tsx # Layout component (optional)88│ ├── _error.tsx # Unified error page (404/500)89│ ├── index.tsx # Home page (/)90│ └── api/ # API routes91├── islands/ # Interactive components (hydrated on client)92│ └── Counter.tsx93├── components/ # Server-only components (no JS shipped)94│ └── Button.tsx95├── static/ # Static assets96└── utils/97 └── state.ts # Define helpers for type safety98```99100**Note:** Fresh 2.x does not use a manifest file, a separate dev entry point, or a separate config file.101102### main.ts (Fresh 2.x Entry Point)103104```typescript105import { App, fsRoutes, staticFiles, trailingSlashes } from "fresh";106107const app = new App()108 .use(staticFiles())109 .use(trailingSlashes("never"));110111await fsRoutes(app, {112 dir: "./",113 loadIsland: (path) => import(`./islands/${path}`),114 loadRoute: (path) => import(`./routes/${path}`),115});116117if (import.meta.main) {118 await app.listen();119}120```121122### vite.config.ts123124```typescript125import { defineConfig } from "vite";126import { fresh } from "@fresh/plugin-vite";127import tailwindcss from "@tailwindcss/vite";128129export default defineConfig({130 plugins: [131 fresh(),132 tailwindcss(),133 ],134});135```136137### deno.json Configuration138139A Fresh 2.x project's deno.json looks like this (created by `jsr:@fresh/init`):140141```json142{143 "tasks": {144 "dev": "vite",145 "build": "vite build",146 "preview": "deno serve -A _fresh/server.js"147 },148 "imports": {149 "fresh": "jsr:@fresh/core@^2",150 "fresh/runtime": "jsr:@fresh/core@^2/runtime",151 "@fresh/plugin-vite": "jsr:@fresh/plugin-vite@^1",152 "@preact/signals": "npm:@preact/signals@^2",153 "preact": "npm:preact@^10",154 "preact/hooks": "npm:preact@^10/hooks",155 "@/": "./"156 }157}158```159160**Adding dependencies:** Use `deno add` to add new packages:161162```sh163deno add jsr:@std/http # JSR packages164deno add npm:@tailwindcss/vite # npm packages165```166167### Import Reference (Fresh 2.x)168169```typescript170// Core Fresh imports171import { App, staticFiles, fsRoutes } from "fresh";172import { trailingSlashes, cors, csp } from "fresh";173import { createDefine, HttpError } from "fresh";174import type { PageProps, Middleware, RouteConfig } from "fresh";175176// Runtime imports (for client-side checks)177import { IS_BROWSER } from "fresh/runtime";178179// Preact180import { useSignal, signal, computed } from "@preact/signals";181import { useState, useEffect, useRef } from "preact/hooks";182```183184### Key Concepts185186**Routes (`routes/` folder)**187- File-based routing: `routes/about.tsx` → `/about`188- Dynamic routes: `routes/blog/[slug].tsx` → `/blog/my-post`189- Optional segments: `routes/docs/[[version]].tsx` → `/docs` or `/docs/v2`190- Catch-all routes: `routes/old/[...path].tsx` → `/old/foo/bar`191- Route groups: `routes/(marketing)/` for shared layouts without URL path changes192193**Layouts (`_app.tsx`)**194```tsx195import type { PageProps } from "fresh";196197export default function App({ Component }: PageProps) {198 return (199 <html>200 <head>201 <meta charset="utf-8" />202 <meta name="viewport" content="width=device-width, initial-scale=1" />203 <title>My App</title>204 </head>205 <body>206 <Component />207 </body>208 </html>209 );210}211```212213**Async Server Components**214```tsx215export default async function Page() {216 const data = await fetchData(); // Runs on server only217 return <div>{data.title}</div>;218}219```220221## Data Fetching Patterns222223Fresh 2.x provides two approaches for fetching data on the server. The handler pattern is the recommended default because it demonstrates the full Fresh 2.x architecture and provides the most flexibility.224225### Approach A: Handler with Data Object (Recommended)226227Use this as the default for data fetching. It uses the full Fresh 2.x handler pattern with typed data passing. Always show the complete setup including `utils/state.ts` when demonstrating this pattern.228229```tsx230// utils/state.ts - one-time setup for type-safe handlers231import { createDefine } from "fresh";232233export interface State {234 user?: { id: string; name: string };235}236237export const define = createDefine<State>();238```239240```tsx241// routes/posts.tsx242import { define } from "@/utils/state.ts";243244// Handler fetches data and returns it via { data: {...} }245export const handler = define.handlers(async (ctx) => {246 const response = await fetch("https://jsonplaceholder.typicode.com/posts");247 const posts = await response.json();248 return { data: { posts } };249});250251// Page receives typed data252export default define.page<typeof handler>(({ data }) => {253 return (254 <div>255 <h1>Posts</h1>256 <ul>257 {data.posts.map((post) => <li key={post.id}>{post.title}</li>)}258 </ul>259 </div>260 );261});262```263264This approach also supports auth checks, redirects, and other logic before rendering.265266### Approach B: Async Server Components (Shorthand)267268For the simplest cases where you just need to fetch and display data with no auth or redirects:269270```tsx271// routes/servers.tsx272export default async function ServersPage() {273 const servers = await db.query("SELECT * FROM servers");274275 return (276 <div>277 <h1>Servers</h1>278 <ul>279 {servers.map((s) => <li key={s.id}>{s.name}</li>)}280 </ul>281 </div>282 );283}284```285286### Decision Guide287288```289Need to fetch data on server?290├─ Yes → Use handler with { data: {...} } return (Approach A)291│ (supports auth checks, redirects, and typed data passing)292├─ Simple DB query, no logic? → Async page component is also fine (Approach B)293└─ No → Just use a regular page component294```295296## Handlers and Define Helpers (Fresh 2.x)297298Fresh 2.x uses a **single context parameter** pattern for handlers. Always use `(ctx)` as the only parameter.299300**Important:** When demonstrating any handler pattern (data fetching, form handling, API routes, auth), always show or reference the `utils/state.ts` setup which imports `createDefine` from `"fresh"`. This ensures the complete Fresh 2.x architecture is visible.301302### Route Handlers303304Always use `define.handlers()` for type-safe route handlers in file-based routes:305306```tsx307// routes/api/users.ts308import { define } from "@/utils/state.ts";309310// Single function handles all methods311export const handler = define.handlers((ctx) => {312 return new Response(`Hello from ${ctx.req.method}`);313});314315// Or method-specific handlers316export const handler = define.handlers({317 GET(ctx) {318 return Response.json({ users: [] });319 },320 async POST(ctx) {321 const body = await ctx.req.json();322 return Response.json({ created: true }, { status: 201 });323 },324});325```326327Note: Bare handler exports (`export const handler = (ctx) => {...}`) also work but lose TypeScript type safety. Prefer `define.handlers()`.328329### The Context Object330331The `ctx` parameter provides everything you need:332333```tsx334export const handler = (ctx) => {335 ctx.req // The Request object336 ctx.url // URL instance with pathname, searchParams337 ctx.params // Route parameters { slug: "my-post" }338 ctx.state // Request-scoped data for middlewares339 ctx.config // Fresh configuration340 ctx.route // Matched route pattern341 ctx.error // Caught error (on error pages)342343 // Methods344 ctx.render(<JSX />) // Render JSX to Response (JSX only, NOT data objects!)345 ctx.render(<JSX />, { status: 201, headers: {...} }) // With response options346 ctx.redirect("/other") // Redirect (302 default)347 ctx.redirect("/other", 301) // Permanent redirect348 ctx.next() // Call next middleware349};350```351352### Define Helpers (Type Safety)353354Create a `utils/state.ts` file for type-safe handlers:355356```tsx357// utils/state.ts358import { createDefine } from "fresh";359360// Define your app's state type361export interface State {362 user?: { id: string; name: string };363}364365// Export typed define helpers366export const define = createDefine<State>();367```368369Use in routes:370371```tsx372// routes/profile.tsx373import { define } from "@/utils/state.ts";374import type { PageProps } from "fresh";375376// Typed handler with data377export const handler = define.handlers((ctx) => {378 if (!ctx.state.user) {379 return ctx.redirect("/login");380 }381 return { data: { user: ctx.state.user } };382});383384// Page receives typed data385export default define.page<typeof handler>(({ data }) => {386 return <h1>Welcome, {data.user.name}!</h1>;387});388```389390### Middleware (Fresh 2.x)391392```tsx393// routes/_middleware.ts394import { define } from "@/utils/state.ts";395396export const handler = define.middleware(async (ctx) => {397 // Before route handler398 console.log(`${ctx.req.method} ${ctx.url.pathname}`);399400 // Call next middleware/route401 const response = await ctx.next();402403 // After route handler404 return response;405});406```407408### API Routes409410```tsx411// routes/api/posts/[id].ts412import { define } from "@/utils/state.ts";413import { HttpError } from "fresh";414415export const handler = define.handlers({416 async GET(ctx) {417 const post = await getPost(ctx.params.id);418 if (!post) {419 throw new HttpError(404); // Uses _error.tsx420 }421 return Response.json(post);422 },423424 async DELETE(ctx) {425 if (!ctx.state.user) {426 throw new HttpError(401);427 }428 await deletePost(ctx.params.id);429 return new Response(null, { status: 204 });430 },431});432```433434## Islands (Interactive Components)435436Islands are components that get hydrated (made interactive) on the client. Place them in the `islands/` folder or `(_islands)` folder within routes.437438### When to Use Islands439440- User interactions (clicks, form inputs)441- Client-side state (counters, toggles)442- Browser APIs (localStorage, geolocation)443444### Island Example445446```tsx447// islands/Counter.tsx448import { useSignal } from "@preact/signals";449450export default function Counter() {451 const count = useSignal(0);452453 return (454 <div>455 <p>Count: {count.value}</p>456 <button onClick={() => count.value++}>457 Increment458 </button>459 </div>460 );461}462```463464### Client-Only Code with IS_BROWSER465466```tsx467// islands/LocalStorageCounter.tsx468import { IS_BROWSER } from "fresh/runtime";469import { useSignal } from "@preact/signals";470471export default function LocalStorageCounter() {472 // Return placeholder during SSR473 if (!IS_BROWSER) {474 return <div>Loading...</div>;475 }476477 // Client-only code478 const stored = localStorage.getItem("count");479 const count = useSignal(stored ? parseInt(stored) : 0);480481 return (482 <button onClick={() => {483 count.value++;484 localStorage.setItem("count", String(count.value));485 }}>486 Count: {count.value}487 </button>488 );489}490```491492### Island Props (Serializable Types)493494Islands can receive these prop types:495- Primitives: string, number, boolean, bigint, undefined, null496- Special values: Infinity, -Infinity, NaN, -0497- Collections: Array, Map, Set498- Objects: Plain objects with string keys499- Built-ins: URL, Date, RegExp, Uint8Array500- Preact: JSX elements, Signals (with serializable values)501- Circular references are supported502503**Functions cannot be passed as props.**504505### Island Rules5065071. **Props must be serializable** - No functions, only JSON-compatible data5082. **Keep islands small** - Less JavaScript shipped to client5093. **Prefer server components** - Only use islands when you need interactivity510511## Preact512513Preact is a 3KB alternative to React. Fresh uses Preact instead of React.514515### Preact vs React Differences516517| Preact | React |518|--------|-------|519| `class` works | `className` required |520| `@preact/signals` | `useState` |521| 3KB bundle | ~40KB bundle |522523### Hooks (Same as React)524525```tsx526import { useState, useEffect, useRef } from "preact/hooks";527528function MyComponent() {529 const [value, setValue] = useState(0);530 const inputRef = useRef<HTMLInputElement>(null);531532 useEffect(() => {533 console.log("Component mounted");534 }, []);535536 return <input ref={inputRef} value={value} />;537}538```539540### Signals (Preact's Reactive State)541542Signals are Preact's more efficient alternative to useState:543544```tsx545import { signal, computed } from "@preact/signals";546547const count = signal(0);548const doubled = computed(() => count.value * 2);549550function Counter() {551 return (552 <div>553 <p>Count: {count}</p>554 <p>Doubled: {doubled}</p>555 <button onClick={() => count.value++}>+1</button>556 </div>557 );558}559```560561Benefits of signals:562- More granular updates (only re-renders what changed)563- Can be defined outside components564- Cleaner code for shared state565566## Tailwind CSS in Fresh (Optional)567568Tailwind CSS is optional—you don't need it to build a great Fresh app. However, many developers prefer it for rapid styling. Fresh 2.x uses Vite for builds, so Tailwind integrates via the Vite plugin.569570### Setup571572Install both Tailwind packages:573574```sh575deno add npm:@tailwindcss/vite npm:tailwindcss576```577578**Important:** You need both packages. `@tailwindcss/vite` is the Vite plugin, and `tailwindcss` is the core library it depends on.579580Configure Vite in `vite.config.ts`:581```typescript582import { defineConfig } from "vite";583import tailwindcss from "@tailwindcss/vite";584585export default defineConfig({586 plugins: [tailwindcss()],587});588```589590Add the Tailwind import to your CSS file (e.g., `assets/styles.css`):591```css592@import "tailwindcss";593```594595Then import this CSS file in your `client.ts`:596```typescript597import "./assets/styles.css";598```599600### Usage601602```tsx603export default function Button({ children }) {604 return (605 <button class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">606 {children}607 </button>608 );609}610```611612### Best Practices6136141. **Prefer utility classes** over `@apply`6152. **Use `class` not `className`** (Preact supports both, but `class` is simpler)6163. **Dark mode**: Use `class` strategy in tailwind.config.js617618```tsx619<div class="bg-white dark:bg-gray-900">620 <p class="text-gray-900 dark:text-white">Hello</p>621</div>622```623624## Building and Deploying625626### Development627628```bash629deno task dev # Start dev server with hot reload (http://127.0.0.1:5173/)630```631632### Production Build633634```bash635deno task build # Build for production636deno task preview # Preview production build locally637```638639### Deploy to Deno Deploy640641```bash642deno task build # Build first643deno deploy --prod # Deploy to production644```645646## Quick Reference647648| Task | Command/Pattern |649|------|-----------------|650| Create Fresh project | `deno run -Ar jsr:@fresh/init` |651| Start dev server | `deno task dev` (port 5173) |652| Build for production | `deno task build` |653| Add a page | Create `routes/pagename.tsx` |654| Add an API route | Create `routes/api/endpoint.ts` |655| Add interactive component | Create `islands/ComponentName.tsx` |656| Add static component | Create `components/ComponentName.tsx` |657658## Common Mistakes659660### Using Fresh 1.x Patterns (Most Common LLM Error)661662**Using old import specifiers**663664The old dollar-sign Fresh import paths and alpha version imports are deprecated. Always use the stable `fresh` package:665666```tsx667// ✅ CORRECT - Fresh 2.x stable imports668import { App, staticFiles } from "fresh";669import type { PageProps } from "fresh";670```671672**Using two-parameter handlers**673674The old two-parameter handler signature is deprecated. Fresh 2.x uses a single context parameter:675676```tsx677// ✅ CORRECT - Fresh 2.x uses single context parameter678export const handler = {679 GET(ctx) { // Single ctx param680 return ctx.render(<MyPage />);681 }682};683```684685**Creating legacy files**686687Fresh 2.x does not use a manifest file, a separate dev entry point, or a separate config file. The correct Fresh 2.x file structure is:688689```690main.ts # Server entry691client.ts # Client entry692vite.config.ts # Vite config693```694695**Using deprecated context methods**696697The old `renderNotFound()`, bare `render()` without JSX, and `basePath` patterns are deprecated. Use these Fresh 2.x patterns instead:698699```tsx700// ✅ CORRECT - Fresh 2.x patterns701throw new HttpError(404)702ctx.render(<MyComponent />)703ctx.config.basePath704```705706**Passing data from handlers to pages (VERY COMMON MISTAKE)**707708This is a frequent error. In Fresh 2.x, you cannot pass data through `ctx.render()`. Instead, return an object with a `data` property from the handler:709710```tsx711// ✅ CORRECT - Return object with data property from handler712export const handler = define.handlers(async (ctx) => {713 const servers = await getServers();714 return { data: { servers } }; // Return { data: {...} } object715});716717// ✅ CORRECT - Link page to handler type with typeof718export default define.page<typeof handler>(({ data }) => {719 return <ul>{data.servers.map((s) => <li>{s.name}</li>)}</ul>;720});721722// ✅ ALSO CORRECT - Use async page component (simpler when no auth/redirects needed)723export default async function ServersPage() {724 const servers = await getServers();725 return <ul>{servers.map((s) => <li>{s.name}</li>)}</ul>;726}727```728729**Old task commands in deno.json**730731The old task commands that reference `dev.ts` are deprecated. Use the Vite-based tasks:732733```json734// ✅ CORRECT - Fresh 2.x tasks735{736 "tasks": {737 "dev": "vite",738 "build": "vite build",739 "preview": "deno serve -A _fresh/server.js"740 }741}742```743744### Island Mistakes745746**Putting too much JavaScript in islands**747```tsx748// ❌ Wrong - entire page as an island (ships all JS to client)749// islands/HomePage.tsx750export default function HomePage() {751 return (752 <div>753 <Header />754 <MainContent />755 <Footer />756 </div>757 );758}759760// ✅ Correct - only interactive parts are islands761// routes/index.tsx (server component)762import Counter from "../islands/Counter.tsx";763764export default function HomePage() {765 return (766 <div>767 <Header />768 <MainContent />769 <Counter />770 <Footer />771 </div>772 );773}774```775776**Passing non-serializable props to islands**777```tsx778// ❌ Wrong - functions can't be serialized779<Counter onUpdate={(val) => console.log(val)} />780781// ✅ Correct - only pass JSON-serializable data782<Counter initialValue={5} label="Click count" />783```784785### Other Common Mistakes786787**Using `className` instead of `class`**788```tsx789// ❌ Works but unnecessary in Preact790<div className="container">791792// ✅ Preact supports native HTML attribute793<div class="container">794```795796**Forgetting to build before deploying Fresh 2.x**797```bash798# ❌ Wrong - Fresh 2.x requires a build step799deno deploy --prod800801# ✅ Correct - build first, then deploy802deno task build803deno deploy --prod804```805806**Creating islands for non-interactive content**807```tsx808// ❌ Wrong - this doesn't need to be an island (no interactivity)809// islands/StaticCard.tsx810export default function StaticCard({ title, body }) {811 return <div class="card"><h2>{title}</h2><p>{body}</p></div>;812}813814// ✅ Correct - use a regular component (no JS shipped)815// components/StaticCard.tsx816export default function StaticCard({ title, body }) {817 return <div class="card"><h2>{title}</h2><p>{body}</p></div>;818}819```820821**Using old Tailwind plugin**822823The old Fresh 1.x Tailwind plugin is deprecated. Fresh 2.x uses the Vite Tailwind plugin:824825```typescript826// ✅ CORRECT - Fresh 2.x uses Vite Tailwind plugin827import tailwindcss from "@tailwindcss/vite";828```829830**Missing tailwindcss package**831```sh832# Error: Can't resolve 'tailwindcss' in '/path/to/assets'833834# ❌ WRONG - Only installed the Vite plugin835deno add npm:@tailwindcss/vite836837# ✅ CORRECT - Install both packages838deno add npm:@tailwindcss/vite npm:tailwindcss839```840841The `@tailwindcss/vite` plugin requires the core `tailwindcss` package to be installed separately.842843## Fresh Alpha Versions (2.0.0-alpha.*)844845Some projects use Fresh 2.x alpha releases (e.g., `@fresh/core@2.0.0-alpha.29`). These are **not Fresh 1.x** but use a different setup than stable Fresh 2.x:846847| Alpha pattern | Stable 2.x pattern |848|---|---|849| `dev.ts` entry point | `vite.config.ts` |850| `@fresh/plugin-tailwind` | `@tailwindcss/vite` |851| `deno run -A --watch dev.ts` | `vite` |852| Dev server on port 8000 | Dev server on port 5173 |853| No `client.ts` | Requires `client.ts` |854| `deno run -A dev.ts build` | `vite build` |855856**IMPORTANT:** If you see `dev.ts` in a project with `@fresh/core@2.0.0-alpha.*` in `deno.json`, do NOT treat it as a Fresh 1.x artifact. It is the correct entry point for alpha versions. Check the `deno.json` imports to determine which version is in use before suggesting changes.857858Alpha projects also use the handler pattern `define.handlers({ GET(ctx) { ... } })` which returns `{ data: {...} }` - this is the same as stable 2.x.859860## Migrating from Fresh 1.x to 2.x861862If you have an existing Fresh 1.x project, run the migration tool:863864```bash865deno run -Ar jsr:@fresh/update866```867868This tool automatically:869- Converts old import paths to the new `fresh` package870- Updates handler signatures to use the single `(ctx)` parameter871- Removes legacy generated and config files872- Creates `vite.config.ts` and `client.ts`873- Updates `deno.json` tasks to use Vite874- Merges separate error pages into unified `_error.tsx`875- Updates deprecated context method calls to Fresh 2.x equivalents876877### Manual Migration Checklist878879If the tool misses anything:8808811. **Imports**: All Fresh imports should come from `fresh` or `fresh/runtime`8822. **Handlers**: Should use single `(ctx)` parameter, access request via `ctx.req`8833. **Files**: Remove any legacy generated files, dev entry points, or old config files8844. **Tasks**: Update to `vite`, `vite build`, `deno serve -A _fresh/server.js`8855. **Error pages**: Use a single unified `_error.tsx`8866. **Tailwind**: Use `@tailwindcss/vite` (the Vite plugin)