com um clique
frontend-routing
Use when creating or modifying routes, route guards, or navigation
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
Use when creating or modifying routes, route guards, or navigation
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Baseado na classificação ocupacional 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