원클릭으로
frontend-routing
Use when creating or modifying routes, route guards, or navigation
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Use when creating or modifying routes, route guards, or navigation
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Use when deploying Cloudflare Workers, managing R2 storage, or working with Cloudflare infrastructure
Use when working with ANTD components, theme tokens, icons, forms, or feedback components (message/notification/modal)
Use when adding, referencing, or serving static assets (images, fonts, videos, 3D models) through the R2 CDN pipeline with type-safe imports
Use when writing or reviewing JavaScript/TypeScript code for style patterns like concise arrows, inline handlers, expression formatting, or when tempted to use eslint-disable
Use when working with environment variables in frontend code
Use when creating or modifying keyboard shortcuts/hotkeys in frontend code
| name | frontend-routing |
| description | Use when creating or modifying routes, route guards, or navigation |
All routing uses TanStack Router with file-based routing. Follow these patterns for route naming, guards, params, and navigation.
src/routes/
├── __root.tsx # Root layout (double underscore)
├── _protected.tsx # Layout route (single underscore)
├── _protected/
│ ├── index.tsx # Index route
│ └── $organizationId.tsx # Dynamic param route
├── _auth.tsx # Layout route
├── _auth/
│ ├── login.tsx # Public route under layout
│ └── signup.tsx # Public route under layout
└── not-whitelisted.tsx # Standalone public route
Naming rules:
__root.tsx (double underscore)_ (e.g., _protected.tsx) — see frontend-route-layout$paramName syntax (e.g., $organizationId.tsx)index.tsx within folderPattern: Use beforeLoad for authentication and authorization checks.
export const Route = createFileRoute("/_protected")({
beforeLoad: async ({ location }) => {
const { data: { session } } = await supabase.auth.getSession();
if (!session) {
const redirectPath = `${location.pathname}${location.search ? `?${new URLSearchParams(location.search).toString()}` : ""}`;
throw redirect({ to: "/login", search: { redirect: redirectPath } });
}
// Authorization — direct DB query, no cache
const { data: profile } = await supabase
.from("profiles")
.select("whitelisted")
.eq("id", session.user.id)
.single();
if (!profile?.whitelisted) {
throw redirect({ to: "/not-whitelisted" });
}
},
component: RouteComponent,
});
Key rules:
beforeLoad for security checks (never component-level)export const Route = createFileRoute("/_protected/$organizationId")({
beforeLoad: async ({ params }) => {
const { organizationId } = params;
const { data: { session } } = await supabase.auth.getSession();
if (!session) throw redirect({ to: "/login" });
const { data: membership } = await supabase
.from("organization_members")
.select("*")
.eq("user_id", session.user.id)
.eq("organization_id", organizationId)
.maybeSingle();
if (!membership) throw redirect({ to: "/access-denied" });
},
component: Page_Organization,
});
maybeSingle() to handle no-match gracefullyconst LoginSearchSchema = z.object({
redirect: z.string().optional().catch(undefined),
});
export const Route = createFileRoute("/_auth/login")({
validateSearch: LoginSearchSchema.parse,
beforeLoad: async ({ search }) => {
const { data: { session } } = await supabase.auth.getSession();
if (session) throw redirect({ to: search.redirect || "/" });
},
component: Page_Login,
});
ALWAYS use the from option for full TypeScript type safety:
// ✅ Correct - Full type safety
const { organizationId, projectId } = useParams({
from: "/_protected/$organizationId/$projectId",
});
// ❌ Wrong - Loses all type safety
const { organizationId } = useParams({ strict: false }) as { organizationId: string };
_root.tsx (single underscore) → ✅ __root.tsx (double):organizationId or {organizationId} → ✅ $organizationIdbeforeLoad/login without preserving path → ✅ Pass location.pathname as search paramuseParams({ strict: false }) → ✅ useParams({ from: '/_protected/$organizationId' })Outlet, _prefix convention