create-page
Scaffold a new page in apps/web/src/app following the project's established pattern. Use when creating any new page or route under the web app.
Best use case
create-page is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Scaffold a new page in apps/web/src/app following the project's established pattern. Use when creating any new page or route under the web app.
Teams using create-page should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/create-page/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How create-page Compares
| Feature / Agent | create-page | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Scaffold a new page in apps/web/src/app following the project's established pattern. Use when creating any new page or route under the web app.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Create Page
Scaffold a new page in `apps/web/src/app/` following the project's established
pattern. All pages are created under the `[locale]` dynamic segment at
`apps/web/src/app/[locale]/`.
## When to Use This Skill
- Any time a new page or route is being created in `apps/web/src/app/`
- Adding a landing page, event page, or any new section to solana.com
- Duplicating a page structure for a new campaign or feature
## Instructions
Create a new page at route: **$ARGUMENTS**
If no route was provided, ask the user for the desired route path (e.g.
`/hackathon`, `/universities/spring-2026`).
### Step 1 — Determine naming conventions
Parse the route to derive:
- **Directory path**: under `apps/web/src/app/[locale]/` (e.g.
`/universities/spring-2026` → `universities/spring-2026/`)
- **Translation namespace**: dot-separated key (e.g. `universities.spring2026`)
- **Component name**: PascalCase (e.g. `Spring2026Page`)
- **File name**: kebab-case (e.g. `spring-2026.tsx`)
### Step 2 — Ask the user what sections the page needs
Unless already specified, ask what content sections are needed (hero, timeline,
CTA, cards, etc.).
### Step 3 — Create the server component (`page.tsx`)
This is the **required** entry point for every page.
Rules:
- Async server component — no `"use client"`
- All `t()` calls happen here, NOT in client components
- Use `t()` for strings, `t.raw()` with type cast for arrays/objects
- Export `generateMetadata` using `getIndexMetadata` from `@/app/metadata`
- Props type is `{ params: Promise<{ locale: string }> }`
- If the page is simple (no client-only hooks or components), render JSX
directly in this file
- If a separate client component is needed, pass a single `translations` object
as its only prop
### Step 4 (optional) — Create a client component (`<page-name>.tsx`)
Only create a separate client component when the page requires client-only
features such as `useState`, `useEffect`, `useRef`, event handlers, browser
APIs, or third-party client-only components.
Rules:
- `"use client"` directive at top
- Named export (not default) matching the import in `page.tsx`
- Define a `*PageProps` interface with a typed `translations` object
- Use existing shared components from `@/components/` where applicable
### Step 5 — Add translation keys
Add English keys to `apps/web/public/locales/en/common.json`:
- Namespace matches the route (e.g. `privacyhack`, `universities`)
- Nested pages use dot-separated keys (e.g. `universities.hackathon.*`)
- Root-level `title` and `description` are used by `generateMetadata`
- Arrays are stored as JSON arrays and accessed via `t.raw()`
- Only edit the `en` locale — other locales are handled by the i18n pipeline
### Step 6 — Verify
Check for TypeScript issues to confirm the page builds without errors.
### Guardrails
- Follow exact patterns from existing pages (`privacyhack/`, `universities/`,
`universities/hackathon-fall-2025/`)
- Keep the scaffold minimal with placeholder translations — do not invent
content
- `page.tsx` and translation keys are always required
- A separate client component is only needed when client-only hooks or
components are used — do not create one unnecessarily
## Examples
### Simple page — server component only (`page.tsx`)
When no client-only features are needed, everything lives in `page.tsx`:
```tsx
import { getTranslations } from "next-intl/server";
import { getIndexMetadata } from "@/app/metadata";
type Props = { params: Promise<{ locale: string }> };
export default async function Page(_props: Props) {
const t = await getTranslations();
return (
<div className="overflow-hidden">
<h1>{t("pageName.hero.title")}</h1>
<p>{t("pageName.hero.subtitle")}</p>
</div>
);
}
export async function generateMetadata({ params }: Props) {
const { locale } = await params;
return await getIndexMetadata({
titleKey: "pageName.title",
descriptionKey: "pageName.description",
path: "/page-name",
locale,
});
}
```
### Page with client component — `page.tsx` + `<page-name>.tsx`
When client-only hooks/components are required, split into two files:
**`page.tsx`** (server component — handles i18n):
```tsx
import { PageNamePage } from "./page-name";
import { getTranslations } from "next-intl/server";
import { getIndexMetadata } from "@/app/metadata";
type Props = { params: Promise<{ locale: string }> };
export default async function Page(_props: Props) {
const t = await getTranslations();
const translations = {
heroTitle: t("pageName.hero.title"),
heroSubtitle: t("pageName.hero.subtitle"),
// ... all translation keys passed as props
};
return <PageNamePage translations={translations} />;
}
export async function generateMetadata({ params }: Props) {
const { locale } = await params;
return await getIndexMetadata({
titleKey: "pageName.title",
descriptionKey: "pageName.description",
path: "/page-name",
locale,
});
}
```
**`<page-name>.tsx`** (client component — uses hooks/browser APIs):
```tsx
"use client";
import React from "react";
interface PageNamePageProps {
translations: {
heroTitle: string;
heroSubtitle: string;
// ... typed translation props
};
}
export function PageNamePage({ translations }: PageNamePageProps) {
return (
<div className="overflow-hidden">{/* Sections using translations.* */}</div>
);
}
```
### Translation keys (`apps/web/public/locales/en/common.json`)
```json
"pageName": {
"title": "Page Title | Solana",
"description": "Meta description for SEO",
"hero": {
"title": "...",
"subtitle": "..."
}
}
```Related Skills
migrate-page
Migrate a legacy page from apps/web/src/pages/ (Pages Router) to apps/web/src/app/ (App Router). Use when moving any existing page from the pages directory to the app directory.
refresh-agent-context
Audit this Turborepo for stale or missing agent-reference docs, then refresh repo and app-level `AGENTS.md` and related onboarding docs using the bundled workspace inventory script. Use when apps, packages, routes, ports, or shared tooling changed and the repo needs a fresh agent-oriented context pass.
llms-txt-generator
Upgrade llmtxt-generator.py by scanning apps/ for doc structure changes, then regenerate llms.txt and llms-en.txt while only adding missing sections. Use when updating the generator, adding new doc sections, or refreshing LLM text files.
landing-page-generator
Generates high-converting Next.js/React landing pages with Tailwind CSS. Uses PAS, AIDA, and BAB frameworks for optimized copy/components (Heroes, Features, Pricing). Focuses on Core Web Vitals/SEO.
create-pr
Alias for sentry-skills:pr-writer. Use when users explicitly ask for "create-pr" or reference the legacy skill name. Redirects to the canonical PR writing workflow.
create-issue-gate
Use when starting a new implementation task and an issue must be created with strict acceptance criteria gating before execution.
create-branch
Create a git branch following Sentry naming conventions. Use when asked to "create a branch", "new branch", "start a branch", "make a branch", "switch to a new branch", or when starting new work on the default branch.
You are a professional Landing page designer who is very friendly and supportive.
This AI skill transforms your agent into a friendly landing page design mentor. It guides beginners through planning and designing a conceptual blueprint for their landing page or personal portfolio.
ui-page
Scaffold a new mobile-first page using StyleSeed Toss layout patterns, section rhythm, and existing shell components.
wiki-page-writer
You are a senior documentation engineer that generates comprehensive technical documentation pages with evidence-based depth.
seo-page
Deep single-page SEO analysis covering on-page elements, content quality, technical meta tags, schema, images, and performance. Use when user says "analyze this page", "check page SEO", or provides a single URL for review.
seo-competitor-pages
Generate SEO-optimized competitor comparison and alternatives pages. Covers "X vs Y" layouts, "alternatives to X" pages, feature matrices, schema markup, and conversion optimization. Use when user says "comparison page", "vs page", "alternatives page", "competitor comparison", or "X vs Y".