Token导航 LogoToken导航

Agent Skill · 前端设计

deno-frontend(Deno frontend 前端)

用于辅助前端页面、组件、样式和交互逻辑的开发与维护。它适合让 Agent 生成或审查 React、Next.js、Vue、Tailwind、CSS 等相关代码,整理组件结构,或定位布局和性能问题。使用时需要结合项目现有设计系统、路由和构建方式,避免只生成孤立片段;涉及页面改动时,应配合本地预览和构建检查确认视觉效果。

deno-frontend 是前端设计类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为操作浏览器;许可证信息还不明确,第三方扫描有提醒项,按安装量在前端设计类的 10,216 个 Skill 里排第 1,852。

总安装 3,585周安装 154GitHub Stars 76下载 1,257核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:deno-frontend(Deno frontend 前端)
来源仓库:https://github.com/denoland/skills
仓库路径:skills/deno-frontend
安装命令:
npx skills add https://github.com/denoland/skills --skill deno-frontend
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/denoland/skills --skill deno-frontend

这个 Skill 是做什么的

前端设计类 Skill 关注页面结构、视觉规范和组件写法,输出通常是可运行的界面代码。

deno-frontend 专注 Deno 前端开发,基于 Fresh 2.x 框架与 Preact + Tailwind CSS 技术组合。采用岛屿架构实现服务端渲染与客户端交互分离,优化首屏加载性能。支持动态导入、信号驱动响应式更新等现代前端模式,适合轻量级 Web 应用。

组件开发需遵循 Fresh 的路由约定,避免破坏默认的 SSR 与静态导出流程。适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

来源仓库内路径:skills/deno-frontend

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/denoland/skills --skill deno-frontend;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要操作浏览器,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

deno-frontend 怎么安装?

在终端执行 npx skills add https://github.com/denoland/skills --skill deno-frontend。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

deno-frontend 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

deno-frontend 需要什么权限?

权限级别是操作浏览器。该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

deno-frontend 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

deno-frontend 用的人多吗?

公开数据是累计安装 3,585 次,近一周安装 154 次,下载 1,257 次,GitHub 76 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

deno-frontend 安全吗?

第三方扫描有提醒项,说明存在需要人工确认的行为。安装前请打开源码看清它读了什么、联网到哪里。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方扫描存在提醒项或未通过项,需要人工逐条确认。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理前端设计类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

前端设计类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
frontend-design

前端设计 · Codex、Claude

8,823,360126,072只读MIT通过
vercel-composition-patterns

Vercel 组合模式 · Codex、Claude

3,858,81625,893需要联网MIT通过
vercel-react-native-skills

Vercel React native skills 前端 · Codex、Claude

2,468,66425,878只读MIT通过
adapt

adapt 搜索 · Codex、Claude

2,027,04023,125操作浏览器未明确通过
bolder

bolder 分析 · Codex、Claude

2,004,28823,130只读未明确通过
deno-frontend

Deno frontend 前端 · 本页

3,58576操作浏览器未明确有提醒

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Deno Frontend Development

Overview

This 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.

When to Use This Skill

  • Creating a new Fresh web application
  • Building interactive UI components (islands)
  • Adding server-rendered pages and routes
  • Integrating Tailwind CSS for styling
  • Choosing between islands (client-side) vs components (server-only)
  • Working with Preact hooks and signals

Apply these practices when building web applications in Deno.

Scope Boundaries

This skill applies only to Fresh/Deno frontend questions. Follow these rules:

  • 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.
  • Do not include Fresh imports, useSignal, island patterns, or Fresh project structure in responses about other frameworks.
  • Only recommend Fresh when the user explicitly asks about Fresh or is working in a Fresh/Deno project.

CRITICAL: Never Show Deprecated Syntax

When 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.

  • Say "Replace the old dollar-sign import paths with stable Fresh 2.x imports" — then show only the correct from "fresh" approach
  • Do NOT write ❌ Old: import {App} from "$fresh/server.ts" — this is never acceptable, even as a negative example
  • 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.

Only demonstrate Fresh 2.x patterns.

CRITICAL: Fresh 2.x vs 1.x

Always use Fresh 2.x patterns. Fresh 1.x is deprecated. Key differences:

  • Fresh 2.x uses import {App} from "fresh" — the old dollar-sign import paths are deprecated
  • Fresh 2.x has no manifest file — the old auto-generated manifest is no longer needed
  • Fresh 2.x uses vite.config.ts for dev — the old dev.ts entry point is gone
  • Fresh 2.x configures via new App() — the old config file is no longer used
  • Fresh 2.x handlers take a single (ctx) parameter — the old two-parameter signature is deprecated
  • Fresh 2.x uses a unified _error.tsx — the old separate error pages are replaced

Always use Fresh 2.x stable imports:

// ✅ CORRECT - Fresh 2.x stable
import { App, staticFiles } from "fresh";
import { define } from "./utils/state.ts"; // Project-local define helpers

Fresh Framework

Reference: https://fresh.deno.dev/docs

Fresh 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.

Creating a Fresh Project

deno run -Ar jsr:@fresh/init
cd my-project
deno task dev    # Runs at http://127.0.0.1:5173/

Project Structure (Fresh 2.x)

my-project/
├── deno.json           # Config, dependencies, and tasks
├── main.ts             # Server entry point
├── client.ts           # Client entry point (CSS imports)
├── vite.config.ts      # Vite configuration
├── routes/             # Pages and API routes
│   ├── _app.tsx        # App layout wrapper (outer HTML)
│   ├── _layout.tsx     # Layout component (optional)
│   ├── _error.tsx      # Unified error page (404/500)
│   ├── index.tsx       # Home page (/)
│   └── api/            # API routes
├── islands/            # Interactive components (hydrated on client)
│   └── Counter.tsx
├── components/         # Server-only components (no JS shipped)
│   └── Button.tsx
├── static/             # Static assets
└── utils/
    └── state.ts        # Define helpers for type safety

Note: Fresh 2.x does not use a manifest file, a separate dev entry point, or a separate config file.

main.ts (Fresh 2.x Entry Point)

import { App, fsRoutes, staticFiles, trailingSlashes } from "fresh";

const app = new App()
  .use(staticFiles())
  .use(trailingSlashes("never"));

await fsRoutes(app, {
  dir: "./",
  loadIsland: (path) => import(`./islands/${path}`),
  loadRoute: (path) => import(`./routes/${path}`),
});

if (import.meta.main) {
  await app.listen();
}

vite.config.ts

import { defineConfig } from "vite";
import { fresh } from "@fresh/plugin-vite";
import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
  plugins: [
    fresh(),
    tailwindcss(),
  ],
});

deno.json Configuration

A Fresh 2.x project's deno.json looks like this (created by jsr:@fresh/init):

{
  "tasks": {
    "dev": "vite",
    "build": "vite build",
    "preview": "deno serve -A _fresh/server.js"
  },
  "imports": {
    "fresh": "jsr:@fresh/core@^2",
    "fresh/runtime": "jsr:@fresh/core@^2/runtime",
    "@fresh/plugin-vite": "jsr:@fresh/plugin-vite@^1",
    "@preact/signals": "npm:@preact/signals@^2",
    "preact": "npm:preact@^10",
    "preact/hooks": "npm:preact@^10/hooks",
    "@/": "./"
  }
}

Adding dependencies: Use deno add to add new packages:

deno add jsr:@std/http          # JSR packages
deno add npm:@tailwindcss/vite  # npm packages

Import Reference (Fresh 2.x)

// Core Fresh imports
import { App, staticFiles, fsRoutes } from "fresh";
import { trailingSlashes, cors, csp } from "fresh";
import { createDefine, HttpError } from "fresh";
import type { PageProps, Middleware, RouteConfig } from "fresh";

// Runtime imports (for client-side checks)
import { IS_BROWSER } from "fresh/runtime";

// Preact
import { useSignal, signal, computed } from "@preact/signals";
import { useState, useEffect, useRef } from "preact/hooks";

Key Concepts

Routes (routes/ folder)

  • File-based routing: routes/about.tsx → /about
  • Dynamic routes: routes/blog/[slug].tsx → /blog/my-post
  • Optional segments: routes/docs/[[version]].tsx → /docs or /docs/v2
  • Catch-all routes: routes/old/[...path].tsx → /old/foo/bar
  • Route groups: routes/(marketing)/ for shared layouts without URL path changes

Layouts (_app.tsx)

import type { PageProps } from "fresh";

export default function App({ Component }: PageProps) {
  return (
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>My App</title>
      </head>
      <body>
        <Component />
      </body>
    </html>
  );
}

Async Server Components

export default async function Page() {
  const data = await fetchData(); // Runs on server only
  return <div>{data.title}</div>;
}

Data Fetching Patterns

Fresh 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.

Approach A: Handler with Data Object (Recommended)

Use 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.

// utils/state.ts - one-time setup for type-safe handlers
import { createDefine } from "fresh";

export interface State {
  user?: { id: string; name: string };
}

export const define = createDefine<State>();
// routes/posts.tsx
import { define } from "@/utils/state.ts";

// Handler fetches data and returns it via { data: {...} }
export const handler = define.handlers(async (ctx) => {
  const response = await fetch("https://jsonplaceholder.typicode.com/posts");
  const posts = await response.json();
  return { data: { posts } };
});

// Page receives typed data
export default define.page<typeof handler>(({ data }) => {
  return (
    <div>
      <h1>Posts</h1>
      <ul>
        {data.posts.map((post) => <li key={post.id}>{post.title}</li>)}
      </ul>
    </div>
  );
});

This approach also supports auth checks, redirects, and other logic before rendering.

Approach B: Async Server Components (Shorthand)

For the simplest cases where you just need to fetch and display data with no auth or redirects:

// routes/servers.tsx
export default async function ServersPage() {
  const servers = await db.query("SELECT * FROM servers");

  return (
    <div>
      <h1>Servers</h1>
      <ul>
        {servers.map((s) => <li key={s.id}>{s.name}</li>)}
      </ul>
    </div>
  );
}

Decision Guide

Need to fetch data on server?
├─ Yes → Use handler with { data: {...} } return (Approach A)
│   (supports auth checks, redirects, and typed data passing)
├─ Simple DB query, no logic? → Async page component is also fine (Approach B)
└─ No → Just use a regular page component

Handlers and Define Helpers (Fresh 2.x)

Fresh 2.x uses a single context parameter pattern for handlers. Always use (ctx) as the only parameter.

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.

Route Handlers

Always use define.handlers() for type-safe route handlers in file-based routes:

// routes/api/users.ts
import { define } from "@/utils/state.ts";

// Single function handles all methods
export const handler = define.handlers((ctx) => {
  return new Response(`Hello from ${ctx.req.method}`);
});

// Or method-specific handlers
export const handler = define.handlers({
  GET(ctx) {
    return Response.json({ users: [] });
  },
  async POST(ctx) {
    const body = await ctx.req.json();
    return Response.json({ created: true }, { status: 201 });
  },
});

Note: Bare handler exports (export const handler = (ctx) => {...}) also work but lose TypeScript type safety. Prefer define.handlers().

The Context Object

The ctx parameter provides everything you need:

export const handler = (ctx) => {
  ctx.req          // The Request object
  ctx.url          // URL instance with pathname, searchParams
  ctx.params       // Route parameters { slug: "my-post" }
  ctx.state        // Request-scoped data for middlewares
  ctx.config       // Fresh configuration
  ctx.route        // Matched route pattern
  ctx.error        // Caught error (on error pages)

  // Methods
  ctx.render(<JSX />)           // Render JSX to Response (JSX only, NOT data objects!)
  ctx.render(<JSX />, { status: 201, headers: {...} })  // With response options
  ctx.redirect("/other")        // Redirect (302 default)
  ctx.redirect("/other", 301)   // Permanent redirect
  ctx.next()                    // Call next middleware
};

Define Helpers (Type Safety)

Create a utils/state.ts file for type-safe handlers:

// utils/state.ts
import { createDefine } from "fresh";

// Define your app's state type
export interface State {
  user?: { id: string; name: string };
}

// Export typed define helpers
export const define = createDefine<State>();

Use in routes:

// routes/profile.tsx
import { define } from "@/utils/state.ts";
import type { PageProps } from "fresh";

// Typed handler with data
export const handler = define.handlers((ctx) => {
  if (!ctx.state.user) {
    return ctx.redirect("/login");
  }
  return { data: { user: ctx.state.user } };
});

// Page receives typed data
export default define.page<typeof handler>(({ data }) => {
  return <h1>Welcome, {data.user.name}!</h1>;
});

Middleware (Fresh 2.x)

// routes/_middleware.ts
import { define } from "@/utils/state.ts";

export const handler = define.middleware(async (ctx) => {
  // Before route handler
  console.log(`${ctx.req.method} ${ctx.url.pathname}`);

  // Call next middleware/route
  const response = await ctx.next();

  // After route handler
  return response;
});

API Routes

// routes/api/posts/[id].ts
import { define } from "@/utils/state.ts";
import { HttpError } from "fresh";

export const handler = define.handlers({
  async GET(ctx) {
    const post = await getPost(ctx.params.id);
    if (!post) {
      throw new HttpError(404); // Uses _error.tsx
    }
    return Response.json(post);
  },

  async DELETE(ctx) {
    if (!ctx.state.user) {
      throw new HttpError(401);
    }
    await deletePost(ctx.params.id);
    return new Response(null, { status: 204 });
  },
});

Islands (Interactive Components)

Islands are components that get hydrated (made interactive) on the client. Place them in the islands/ folder or (_islands) folder within routes.

When to Use Islands

  • User interactions (clicks, form inputs)
  • Client-side state (counters, toggles)
  • Browser APIs (localStorage, geolocation)

Island Example

// 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>
  );
}

Client-Only Code with IS_BROWSER

// islands/LocalStorageCounter.tsx
import { IS_BROWSER } from "fresh/runtime";
import { useSignal } from "@preact/signals";

export default function LocalStorageCounter() {
  // Return placeholder during SSR
  if (!IS_BROWSER) {
    return <div>Loading...</div>;
  }

  // Client-only code
  const stored = localStorage.getItem("count");
  const count = useSignal(stored ? parseInt(stored) : 0);

  return (
    <button onClick={() => {
      count.value++;
      localStorage.setItem("count", String(count.value));
    }}>
      Count: {count.value}
    </button>
  );
}

Island Props (Serializable Types)

Islands can receive these prop types:

  • Primitives: string, number, boolean, bigint, undefined, null
  • Special values: Infinity, -Infinity, NaN, -0
  • Collections: Array, Map, Set
  • Objects: Plain objects with string keys
  • Built-ins: URL, Date, RegExp, Uint8Array
  • Preact: JSX elements, Signals (with serializable values)
  • Circular references are supported

Functions cannot be passed as props.

Island Rules

  1. Props must be serializable - No functions, only JSON-compatible data
  2. Keep islands small - Less JavaScript shipped to client
  3. Prefer server components - Only use islands when you need interactivity

Preact

Preact is a 3KB alternative to React. Fresh uses Preact instead of React.

Preact vs React Differences

PreactReact
class worksclassName required
@preact/signalsuseState
3KB bundle~40KB bundle

Hooks (Same as React)

import { useState, useEffect, useRef } from "preact/hooks";

function MyComponent() {
  const [value, setValue] = useState(0);
  const inputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    console.log("Component mounted");
  }, []);

  return <input ref={inputRef} value={value} />;
}

Signals (Preact's Reactive State)

Signals are Preact's more efficient alternative to useState:

import { signal, computed } from "@preact/signals";

const count = signal(0);
const doubled = computed(() => count.value * 2);

function Counter() {
  return (
    <div>
      <p>Count: {count}</p>
      <p>Doubled: {doubled}</p>
      <button onClick={() => count.value++}>+1</button>
    </div>
  );
}

Benefits of signals:

  • More granular updates (only re-renders what changed)
  • Can be defined outside components
  • Cleaner code for shared state

Tailwind CSS in Fresh (Optional)

Tailwind 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.

Setup

Install both Tailwind packages:

deno add npm:@tailwindcss/vite npm:tailwindcss

Important: You need both packages. @tailwindcss/vite is the Vite plugin, and tailwindcss is the core library it depends on.

Configure Vite in vite.config.ts:

import { defineConfig } from "vite";
import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
  plugins: [tailwindcss()],
});

Add the Tailwind import to your CSS file (e.g., assets/styles.css):

@import "tailwindcss";

Then import this CSS file in your client.ts:

import "./assets/styles.css";

Usage

export default function Button({ children }) {
  return (
    <button class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
      {children}
    </button>
  );
}

Best Practices

  1. Prefer utility classes over @apply
  2. Use class not className (Preact supports both, but class is simpler)
  3. Dark mode: Use class strategy in tailwind.config.js
<div class="bg-white dark:bg-gray-900">
  <p class="text-gray-900 dark:text-white">Hello</p>
</div>

Building and Deploying

Development

deno task dev      # Start dev server with hot reload (http://127.0.0.1:5173/)

Production Build

deno task build    # Build for production
deno task preview  # Preview production build locally

Deploy to Deno Deploy

deno task build           # Build first
deno deploy --prod        # Deploy to production

Quick Reference

TaskCommand/Pattern
Create Fresh projectdeno run -Ar jsr:@fresh/init
Start dev serverdeno task dev (port 5173)
Build for productiondeno task build
Add a pageCreate routes/pagename.tsx
Add an API routeCreate routes/api/endpoint.ts
Add interactive componentCreate islands/ComponentName.tsx
Add static componentCreate components/ComponentName.tsx

Common Mistakes

Using Fresh 1.x Patterns (Most Common LLM Error)

Using old import specifiers

The old dollar-sign Fresh import paths and alpha version imports are deprecated. Always use the stable fresh package:

// ✅ CORRECT - Fresh 2.x stable imports
import { App, staticFiles } from "fresh";
import type { PageProps } from "fresh";

Using two-parameter handlers

The old two-parameter handler signature is deprecated. Fresh 2.x uses a single context parameter:

// ✅ CORRECT - Fresh 2.x uses single context parameter
export const handler = {
  GET(ctx) {  // Single ctx param
    return ctx.render(<MyPage />);
  }
};

Creating legacy files

Fresh 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:

main.ts           # Server entry
client.ts         # Client entry
vite.config.ts    # Vite config

Using deprecated context methods

The old renderNotFound(), bare render() without JSX, and basePath patterns are deprecated. Use these Fresh 2.x patterns instead:

// ✅ CORRECT - Fresh 2.x patterns
throw new HttpError(404)
ctx.render(<MyComponent />)
ctx.config.basePath

Passing data from handlers to pages (VERY COMMON MISTAKE)

This 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:

// ✅ CORRECT - Return object with data property from handler
export const handler = define.handlers(async (ctx) => {
  const servers = await getServers();
  return { data: { servers } };  // Return { data: {...} } object
});

// ✅ CORRECT - Link page to handler type with typeof
export default define.page<typeof handler>(({ data }) => {
  return <ul>{data.servers.map((s) => <li>{s.name}</li>)}</ul>;
});

// ✅ ALSO CORRECT - Use async page component (simpler when no auth/redirects needed)
export default async function ServersPage() {
  const servers = await getServers();
  return <ul>{servers.map((s) => <li>{s.name}</li>)}</ul>;
}

Old task commands in deno.json

The old task commands that reference dev.ts are deprecated. Use the Vite-based tasks:

// ✅ CORRECT - Fresh 2.x tasks
{
  "tasks": {
    "dev": "vite",
    "build": "vite build",
    "preview": "deno serve -A _fresh/server.js"
  }
}

Island Mistakes

Putting too much JavaScript in islands

// ❌ Wrong - entire page as an island (ships all JS to client)
// islands/HomePage.tsx
export default function HomePage() {
  return (
    <div>
      <Header />
      <MainContent />
      <Footer />
    </div>
  );
}

// ✅ Correct - only interactive parts are islands
// routes/index.tsx (server component)
import Counter from "../islands/Counter.tsx";

export default function HomePage() {
  return (
    <div>
      <Header />
      <MainContent />
      <Counter />
      <Footer />
    </div>
  );
}

Passing non-serializable props to islands

// ❌ Wrong - functions can't be serialized
<Counter onUpdate={(val) => console.log(val)} />

// ✅ Correct - only pass JSON-serializable data
<Counter initialValue={5} label="Click count" />

Other Common Mistakes

Using className instead of class

// ❌ Works but unnecessary in Preact
<div className="container">

// ✅ Preact supports native HTML attribute
<div class="container">

Forgetting to build before deploying Fresh 2.x

# ❌ Wrong - Fresh 2.x requires a build step
deno deploy --prod

# ✅ Correct - build first, then deploy
deno task build
deno deploy --prod

Creating islands for non-interactive content

// ❌ Wrong - this doesn't need to be an island (no interactivity)
// islands/StaticCard.tsx
export default function StaticCard({ title, body }) {
  return <div class="card"><h2>{title}</h2><p>{body}</p></div>;
}

// ✅ Correct - use a regular component (no JS shipped)
// components/StaticCard.tsx
export default function StaticCard({ title, body }) {
  return <div class="card"><h2>{title}</h2><p>{body}</p></div>;
}

Using old Tailwind plugin

The old Fresh 1.x Tailwind plugin is deprecated. Fresh 2.x uses the Vite Tailwind plugin:

// ✅ CORRECT - Fresh 2.x uses Vite Tailwind plugin
import tailwindcss from "@tailwindcss/vite";

Missing tailwindcss package

# Error: Can't resolve 'tailwindcss' in '/path/to/assets'

# ❌ WRONG - Only installed the Vite plugin
deno add npm:@tailwindcss/vite

# ✅ CORRECT - Install both packages
deno add npm:@tailwindcss/vite npm:tailwindcss

The @tailwindcss/vite plugin requires the core tailwindcss package to be installed separately.

Fresh Alpha Versions (2.0.0-alpha.*)

Some 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:

Alpha patternStable 2.x pattern
dev.ts entry pointvite.config.ts
@fresh/plugin-tailwind@tailwindcss/vite
deno run -A --watch dev.tsvite
Dev server on port 8000Dev server on port 5173
No client.tsRequires client.ts
deno run -A dev.ts buildvite build

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.

Alpha projects also use the handler pattern define.handlers({GET(ctx) {...}}) which returns {data: {...}} - this is the same as stable 2.x.

Migrating from Fresh 1.x to 2.x

If you have an existing Fresh 1.x project, run the migration tool:

deno run -Ar jsr:@fresh/update

This tool automatically:

  • Converts old import paths to the new fresh package
  • Updates handler signatures to use the single (ctx) parameter
  • Removes legacy generated and config files
  • Creates vite.config.ts and client.ts
  • Updates deno.json tasks to use Vite
  • Merges separate error pages into unified _error.tsx
  • Updates deprecated context method calls to Fresh 2.x equivalents

Manual Migration Checklist

If the tool misses anything:

  1. Imports: All Fresh imports should come from fresh or fresh/runtime
  2. Handlers: Should use single (ctx) parameter, access request via ctx.req
  3. Files: Remove any legacy generated files, dev entry points, or old config files
  4. Tasks: Update to vite, vite build, deno serve -A _fresh/server.js
  5. Error pages: Use a single unified _error.tsx
  6. Tailwind: Use @tailwindcss/vite (the Vite plugin)

平台分布

Codex

37.44%
按下载量换算471

Claude

27.63%
按下载量换算347

Cursor

18.21%
按下载量换算229

Gemini CLI

9.33%
按下载量换算117

安全审计

Gen Agent Trust Hub

通过

Socket

可疑

Snyk

可疑

权限和风险

操作浏览器

该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在提醒或未通过项,安装前请自行复核。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills