golid / rules
golid-ai/golid/.cursor/rules/solidjs-pages.mdc
SolidJS route UI — Switch/Match, modals, auth gates; never nested Show or early returns
Cursor rule40 starsChanged 4 months ago
---
description: SolidJS route UI — Switch/Match, modals, auth gates; never nested Show or early returns
globs: frontend/src/routes/**/*.tsx
alwaysApply: false
---
# SolidJS Page Patterns
> **Thesis:** Route UI uses flat `Switch/Match`, signal modals, and auth gates — never nested Show or early returns.
## Signal-Driven Modals
Detail views within list pages use a signal, not a sub-route:
```tsx
const [activeItem, setActiveItem] = createSignal<Item | null>(null);
// Open: setActiveItem(item)
// Close: setActiveItem(null)
<Show when={activeItem()}>
{(item) => <DetailModal item={item()} onClose={() => setActiveItem(null)} />}
</Show>
```
## Destructive Actions
Never `window.confirm()`. Always use `DestructiveModal`:
```tsx
const [deleteTarget, setDeleteTarget] = createSignal(false);
// ...
<DestructiveModal
open={deleteTarget()}
onOpenChange={(open) => {
if (!open) setDeleteTarget(false);
}}
onConfirm={handleDelete}
title="Delete item?"
message="This action cannot be undone."
confirmText="Delete"
/>;
```
## Page Titles
**Never put reactive expressions directly inside `<Title>`.** The `@solidjs/meta` `<Title>` component creates an internal computation to track children. During route transitions, this computation is re-evaluated outside the component's reactive root, causing "computations created outside createRoot" warnings.
```tsx
// BAD — reactive ternary inside <Title> leaks during route transition
<Title>{userType() === "admin" ? "Manage Items" : "Items"} | My App</Title>
// GOOD — pre-compute as a memo, pass resolved string (static strings are fine when no reactive expression is needed)
const pageTitle = createMemo(() =>
`${userType() === "admin" ? "Manage Items" : "Items"} | My App`
);
// ...
<Title>{pageTitle()}</Title>
```
## Auth & Routing
- Add every new private route to `PRIVATE_ROUTES` in `frontend/src/lib/constants.ts`.
- Do not early-return from route components before reactive setup. Put auth/access
redirects in an effect and gate JSX with `<Show>` so async `auth.user` updates
can re-render the page.
```tsx
createEffect(
on(
() => auth.user?.type,
(type) => {
if (type && type !== "admin") navigate("/dashboard", { replace: true });
},
),
);
return (
<Show when={auth.user?.type === "admin"} fallback={<Spinner />}>
<AdminPageContent />
</Show>
);
```
## Build Recovery
Stale chunk recovery is owned by `frontend/src/app.tsx` and `frontend/src/lib/chunk-recovery.ts`. Do not add route-level ErrorBoundaries that swallow `Failed to fetch dynamically imported module` or related chunk errors. New boundaries must rethrow chunk errors so the app shell can cache-bust and reload the current build.
## Content States
**NEVER nest `<Show>` for loading/error/empty/data states.** Nested `<Show>` creates stacked reactive scopes that leak computations during route transitions ("computations created outside createRoot" warning).
```tsx
// BAD — 4 nested reactive scopes, leaks on route transition
<Show when={!loading()} fallback={<Spinner />}>
<Show when={error()}><ErrorCard /></Show>
<Show when={!error() && items().length > 0} fallback={
<Show when={!error()}><EmptyState /></Show>
}>
<For each={items()}>{(item) => <Card />}</For>
</Show>
</Show>
// GOOD — flat, one active scope at a time
<Switch>
<Match when={loading()}><Spinner /></Match>
<Match when={error()}><ErrorCard /></Match>
<Match when={items().length === 0}><EmptyState /></Match>
<Match when={items().length > 0}>
<For each={items()}>{(item) => <Card />}</For>
</Match>
</Switch>
```
Import SolidJS `Switch, Match` from `"solid-js"` — not the component-library toggle; alias if both needed (e.g. `Switch as ToggleSwitch`). Reference: Dashboard and Settings pages use flat `Switch/Match` for content states.
## Layout and Components
Keep route files focused on behavior. Layout/styling cookbook: `docs/patterns/solidjs/1-Page.md` ("Golid route patterns"). Component selection, barrel exports, and raw-HTML exceptions: `frontend-components`.
## Related Rules
- Data fetching — see `solidjs-data-fetching`. Files over 600 lines — see `refactor-large-files`.
- Form submission and error display — see `frontend-forms`. Component selection — see `frontend-components`.
Discussion
Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.
Posts are public.Sign in to post
No one has posted yet. Be the first.

