-
-
Couldn't load subscription status.
- Fork 1.3k
docs(solid-start): add Supabase example #5598
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughAdds a new "Basic + Supabase" example entry to documentation configuration and introduces a complete Solid Start example project featuring Supabase authentication, server functions, file-based routing, and a posts CRUD application. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Browser
participant RootRoute as Root Route<br/>(__root.tsx)
participant AuthRoute as Auth Route<br/>(_authed.tsx)
participant SupabaseClient as Supabase<br/>Server Client
participant LoginComp as Login Component
User->>Browser: Visit /posts
Browser->>RootRoute: Request with cookies
RootRoute->>SupabaseClient: fetchUser() - GET current user
SupabaseClient-->>RootRoute: User email or null
alt User Not Authenticated
RootRoute->>AuthRoute: beforeLoad guard
AuthRoute-->>AuthRoute: Throws "Not authenticated"
AuthRoute->>LoginComp: Render error component
LoginComp-->>Browser: Show Login form
User->>LoginComp: Enter credentials & submit
LoginComp->>LoginComp: loginMutation.mutate(email, password)
LoginComp->>SupabaseClient: loginFn (POST via server)
SupabaseClient-->>LoginComp: { error? } or success
alt Login Success
LoginComp->>Browser: Navigate to /
Browser->>RootRoute: User context now available
RootRoute-->>Browser: Show authenticated layout
else Login Failed
LoginComp-->>Browser: Show error + "Sign up instead?" option
end
else User Authenticated
AuthRoute->>Browser: Allow navigation
Browser->>Browser: Render posts list
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Areas requiring extra attention:
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 11
🧹 Nitpick comments (8)
examples/solid/start-supabase-basic/src/components/Login.tsx (4)
13-19: Make success check explicitctx.data is undefined on success. Using !ctx.data?.error works but is implicit. Prefer explicit nullish check.
- onSuccess: async (ctx) => { - if (!ctx.data?.error) { + onSuccess: async (ctx) => { + // Success: server returned nothing (undefined) + if (ctx.data == null) { await router.invalidate() router.navigate({ to: '/' }) return } },
31-39: Guard empty credentials before mutateAvoid hitting the server with empty email/password.
- onSubmit={(e) => { + onSubmit={(e) => { const formData = new FormData(e.target as HTMLFormElement) + const email = String(formData.get('email') || '').trim() + const password = String(formData.get('password') || '') + if (!email || !password) return - - loginMutation.mutate({ + loginMutation.mutate({ data: { - email: formData.get('email') as string, - password: formData.get('password') as string, + email, + password, }, }) }}
41-67: Avoid brittle string matching on Supabase errorsMatching 'Invalid login credentials' is fragile. Return a stable code from the server and branch on that.
Follow-up changes (server + client):
- Server (examples/solid/start-supabase-basic/src/routes/_authed.tsx):
- if (error) { - return { - error: true, - message: error.message, - } - } + if (error) { + return { + error: true as const, + code: error.status ?? 'auth_invalid_credentials', + message: error.message, + } + }
- Client (this file):
- {loginMutation.data()?.error && - loginMutation.data()?.message === 'Invalid login credentials' ? ( + {loginMutation.data()?.error && + (loginMutation.data() as any)?.code === 'auth_invalid_credentials' ? (If you prefer not to change the server payload, at least check loginMutation.status() === 'error' instead of matching message text.
54-60: Ensure signup redirect works from client-invoked server fnsignupFn throws redirect on success. Confirm createServerFn + useServerFn propagates client-side navigation; if not, add onSuccess to navigate.
- const signupMutation = useMutation({ - fn: useServerFn(signupFn), - }) + const signupMutation = useMutation({ + fn: useServerFn(signupFn), + onSuccess: async () => { + // Fallback if redirect isn't auto-followed + await router.invalidate() + router.navigate({ to: '/' }) + }, + })examples/solid/start-supabase-basic/src/hooks/useMutation.ts (1)
3-6: Consider onError/onSettled callbacks and a reset helperNice-to-have: add onError/onSettled callbacks and expose a reset() to return to 'idle'.
If interested, I can draft a small addition without breaking current API.
Also applies to: 34-41
examples/solid/start-supabase-basic/src/routes/login.tsx (1)
4-10: Drop the wrapper; use component: Login directlyThe extra wrapper adds indirection without benefit.
export const Route = createFileRoute('/login')({ - component: LoginComp, + component: Login, }) -function LoginComp() { - return <Login /> -}examples/solid/start-supabase-basic/src/utils/seo.ts (1)
12-32: Filter out tags with undefined contentCurrent output can include meta tags with content: undefined, which is noisy.
- const tags = [ + const tags = [ { title }, { name: 'description', content: description }, { name: 'keywords', content: keywords }, { name: 'twitter:title', content: title }, { name: 'twitter:description', content: description }, { name: 'twitter:creator', content: '@tannerlinsley' }, { name: 'twitter:site', content: '@tannerlinsley' }, { name: 'og:type', content: 'website' }, { name: 'og:title', content: title }, { name: 'og:description', content: description }, ...(image ? [ { name: 'twitter:image', content: image }, { name: 'twitter:card', content: 'summary_large_image' }, { name: 'og:image', content: image }, ] : []), - ] + ].filter((t) => !('content' in t) || t.content !== undefined)examples/solid/start-supabase-basic/src/components/NotFound.tsx (1)
4-26: LGTM; optional: add a data-testid for e2e parityMatches other examples. If you want parity with e2e tests, add data-testid on the root wrapper.
- return ( - <div class="space-y-2 p-2"> + return ( + <div class="space-y-2 p-2" data-testid="default-not-found-component">
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (25)
examples/solid/start-supabase-basic/.env(1 hunks)examples/solid/start-supabase-basic/.gitignore(1 hunks)examples/solid/start-supabase-basic/.prettierignore(1 hunks)examples/solid/start-supabase-basic/package.json(1 hunks)examples/solid/start-supabase-basic/src/components/Auth.tsx(1 hunks)examples/solid/start-supabase-basic/src/components/DefaultCatchBoundary.tsx(1 hunks)examples/solid/start-supabase-basic/src/components/Login.tsx(1 hunks)examples/solid/start-supabase-basic/src/components/NotFound.tsx(1 hunks)examples/solid/start-supabase-basic/src/hooks/useMutation.ts(1 hunks)examples/solid/start-supabase-basic/src/router.tsx(1 hunks)examples/solid/start-supabase-basic/src/routes/__root.tsx(1 hunks)examples/solid/start-supabase-basic/src/routes/_authed.tsx(1 hunks)examples/solid/start-supabase-basic/src/routes/_authed/posts.$postId.tsx(1 hunks)examples/solid/start-supabase-basic/src/routes/_authed/posts.index.tsx(1 hunks)examples/solid/start-supabase-basic/src/routes/_authed/posts.tsx(1 hunks)examples/solid/start-supabase-basic/src/routes/index.tsx(1 hunks)examples/solid/start-supabase-basic/src/routes/login.tsx(1 hunks)examples/solid/start-supabase-basic/src/routes/logout.tsx(1 hunks)examples/solid/start-supabase-basic/src/routes/signup.tsx(1 hunks)examples/solid/start-supabase-basic/src/styles/app.css(1 hunks)examples/solid/start-supabase-basic/src/utils/posts.ts(1 hunks)examples/solid/start-supabase-basic/src/utils/seo.ts(1 hunks)examples/solid/start-supabase-basic/src/utils/supabase.ts(1 hunks)examples/solid/start-supabase-basic/tsconfig.json(1 hunks)examples/solid/start-supabase-basic/vite.config.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use TypeScript in strict mode with extensive type safety across the codebase
Files:
examples/solid/start-supabase-basic/src/routes/index.tsxexamples/solid/start-supabase-basic/src/components/DefaultCatchBoundary.tsxexamples/solid/start-supabase-basic/src/routes/_authed/posts.index.tsxexamples/solid/start-supabase-basic/src/components/NotFound.tsxexamples/solid/start-supabase-basic/src/utils/supabase.tsexamples/solid/start-supabase-basic/src/routes/logout.tsxexamples/solid/start-supabase-basic/src/components/Login.tsxexamples/solid/start-supabase-basic/src/utils/posts.tsexamples/solid/start-supabase-basic/src/hooks/useMutation.tsexamples/solid/start-supabase-basic/src/router.tsxexamples/solid/start-supabase-basic/src/routes/signup.tsxexamples/solid/start-supabase-basic/src/routes/login.tsxexamples/solid/start-supabase-basic/src/components/Auth.tsxexamples/solid/start-supabase-basic/src/utils/seo.tsexamples/solid/start-supabase-basic/src/routes/_authed.tsxexamples/solid/start-supabase-basic/src/routes/__root.tsxexamples/solid/start-supabase-basic/src/routes/_authed/posts.$postId.tsxexamples/solid/start-supabase-basic/vite.config.tsexamples/solid/start-supabase-basic/src/routes/_authed/posts.tsx
**/src/routes/**
📄 CodeRabbit inference engine (AGENTS.md)
Place file-based routes under src/routes/ directories
Files:
examples/solid/start-supabase-basic/src/routes/index.tsxexamples/solid/start-supabase-basic/src/routes/_authed/posts.index.tsxexamples/solid/start-supabase-basic/src/routes/logout.tsxexamples/solid/start-supabase-basic/src/routes/signup.tsxexamples/solid/start-supabase-basic/src/routes/login.tsxexamples/solid/start-supabase-basic/src/routes/_authed.tsxexamples/solid/start-supabase-basic/src/routes/__root.tsxexamples/solid/start-supabase-basic/src/routes/_authed/posts.$postId.tsxexamples/solid/start-supabase-basic/src/routes/_authed/posts.tsx
examples/{react,solid}/**
📄 CodeRabbit inference engine (AGENTS.md)
Keep example applications under examples/react/ and examples/solid/
Files:
examples/solid/start-supabase-basic/src/routes/index.tsxexamples/solid/start-supabase-basic/src/components/DefaultCatchBoundary.tsxexamples/solid/start-supabase-basic/src/routes/_authed/posts.index.tsxexamples/solid/start-supabase-basic/src/components/NotFound.tsxexamples/solid/start-supabase-basic/package.jsonexamples/solid/start-supabase-basic/src/styles/app.cssexamples/solid/start-supabase-basic/tsconfig.jsonexamples/solid/start-supabase-basic/src/utils/supabase.tsexamples/solid/start-supabase-basic/src/routes/logout.tsxexamples/solid/start-supabase-basic/src/components/Login.tsxexamples/solid/start-supabase-basic/src/utils/posts.tsexamples/solid/start-supabase-basic/src/hooks/useMutation.tsexamples/solid/start-supabase-basic/src/router.tsxexamples/solid/start-supabase-basic/src/routes/signup.tsxexamples/solid/start-supabase-basic/src/routes/login.tsxexamples/solid/start-supabase-basic/src/components/Auth.tsxexamples/solid/start-supabase-basic/src/utils/seo.tsexamples/solid/start-supabase-basic/src/routes/_authed.tsxexamples/solid/start-supabase-basic/src/routes/__root.tsxexamples/solid/start-supabase-basic/src/routes/_authed/posts.$postId.tsxexamples/solid/start-supabase-basic/vite.config.tsexamples/solid/start-supabase-basic/src/routes/_authed/posts.tsx
**/package.json
📄 CodeRabbit inference engine (AGENTS.md)
Use workspace:* protocol for internal dependencies in package.json files
Files:
examples/solid/start-supabase-basic/package.json
🧠 Learnings (1)
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript in strict mode with extensive type safety across the codebase
Applied to files:
examples/solid/start-supabase-basic/tsconfig.json
🧬 Code graph analysis (14)
examples/solid/start-supabase-basic/src/routes/index.tsx (1)
examples/solid/start-supabase-basic/src/routes/__root.tsx (1)
Route(32-87)
examples/solid/start-supabase-basic/src/components/DefaultCatchBoundary.tsx (3)
e2e/solid-start/server-routes/src/components/DefaultCatchBoundary.tsx (1)
DefaultCatchBoundary(10-53)examples/solid/start-basic-solid-query/src/components/DefaultCatchBoundary.tsx (1)
DefaultCatchBoundary(10-53)examples/solid/start-basic/src/components/DefaultCatchBoundary.tsx (1)
DefaultCatchBoundary(10-53)
examples/solid/start-supabase-basic/src/routes/_authed/posts.index.tsx (4)
examples/solid/start-supabase-basic/src/routes/__root.tsx (1)
Route(32-87)examples/solid/start-supabase-basic/src/routes/_authed.tsx (1)
Route(23-35)examples/solid/start-supabase-basic/src/routes/_authed/posts.$postId.tsx (1)
Route(6-13)examples/solid/start-supabase-basic/src/routes/_authed/posts.tsx (1)
Route(4-7)
examples/solid/start-supabase-basic/src/components/NotFound.tsx (5)
examples/solid/start-basic-solid-query/src/components/NotFound.tsx (1)
NotFound(3-25)examples/solid/start-basic/src/components/NotFound.tsx (1)
NotFound(3-25)examples/solid/start-basic-cloudflare/src/components/NotFound.tsx (1)
NotFound(3-25)examples/solid/start-basic-nitro/src/components/NotFound.tsx (1)
NotFound(3-25)e2e/solid-start/server-routes/src/components/NotFound.tsx (1)
NotFound(3-25)
examples/solid/start-supabase-basic/src/utils/supabase.ts (1)
packages/start-server-core/src/request-response.ts (2)
getCookies(213-216)setCookie(239-246)
examples/solid/start-supabase-basic/src/routes/logout.tsx (1)
examples/solid/start-supabase-basic/src/utils/supabase.ts (1)
getSupabaseServerClient(4-24)
examples/solid/start-supabase-basic/src/components/Login.tsx (4)
examples/solid/start-supabase-basic/src/hooks/useMutation.ts (1)
useMutation(3-42)examples/solid/start-supabase-basic/src/routes/_authed.tsx (1)
loginFn(6-21)examples/solid/start-supabase-basic/src/routes/signup.tsx (1)
signupFn(7-28)examples/solid/start-supabase-basic/src/components/Auth.tsx (1)
Auth(3-54)
examples/solid/start-supabase-basic/src/routes/signup.tsx (3)
examples/solid/start-supabase-basic/src/utils/supabase.ts (1)
getSupabaseServerClient(4-24)examples/solid/start-supabase-basic/src/hooks/useMutation.ts (1)
useMutation(3-42)examples/solid/start-supabase-basic/src/components/Auth.tsx (1)
Auth(3-54)
examples/solid/start-supabase-basic/src/routes/login.tsx (2)
examples/solid/start-supabase-basic/src/routes/_authed.tsx (1)
Route(23-35)examples/solid/start-supabase-basic/src/components/Login.tsx (1)
Login(8-72)
examples/solid/start-supabase-basic/src/utils/seo.ts (1)
examples/solid/start-basic-netlify/src/utils/seo.ts (1)
title(1-33)
examples/solid/start-supabase-basic/src/routes/_authed.tsx (3)
examples/solid/start-supabase-basic/src/utils/supabase.ts (1)
getSupabaseServerClient(4-24)examples/solid/start-supabase-basic/src/routes/__root.tsx (1)
Route(32-87)examples/solid/start-supabase-basic/src/components/Login.tsx (1)
Login(8-72)
examples/solid/start-supabase-basic/src/routes/__root.tsx (4)
examples/solid/start-supabase-basic/src/utils/supabase.ts (1)
getSupabaseServerClient(4-24)examples/solid/start-supabase-basic/src/utils/seo.ts (1)
seo(1-33)examples/solid/start-supabase-basic/src/components/DefaultCatchBoundary.tsx (1)
DefaultCatchBoundary(10-53)examples/solid/start-supabase-basic/src/components/NotFound.tsx (1)
NotFound(4-26)
examples/solid/start-supabase-basic/src/routes/_authed/posts.$postId.tsx (3)
examples/solid/start-supabase-basic/src/routes/_authed.tsx (1)
Route(23-35)examples/solid/start-supabase-basic/src/utils/posts.ts (1)
fetchPost(11-27)examples/solid/start-supabase-basic/src/components/NotFound.tsx (1)
NotFound(4-26)
examples/solid/start-supabase-basic/src/routes/_authed/posts.tsx (5)
examples/solid/start-supabase-basic/src/routes/__root.tsx (1)
Route(32-87)examples/solid/start-supabase-basic/src/routes/_authed.tsx (1)
Route(23-35)examples/solid/start-supabase-basic/src/routes/_authed/posts.$postId.tsx (1)
Route(6-13)examples/solid/start-supabase-basic/src/routes/_authed/posts.index.tsx (1)
Route(3-5)examples/solid/start-supabase-basic/src/utils/posts.ts (1)
fetchPosts(29-37)
🪛 dotenv-linter (4.0.0)
examples/solid/start-supabase-basic/.env
[warning] 2-2: [UnorderedKey] The SUPABASE_ANON_KEY key should go before the SUPABASE_URL key
(UnorderedKey)
🔇 Additional comments (3)
examples/solid/start-supabase-basic/.prettierignore (1)
1-4: Configuration looks good.The ignore patterns are appropriate for a Solid Start project:
build/andpublic/are standard directories,pnpm-lock.yamlis a tool-generated lock file, androuteTree.gen.tsis a generated router configuration that should not be formatted.examples/solid/start-supabase-basic/src/routes/_authed/posts.index.tsx (1)
3-9: LGTMIndex child route and component are straightforward and correct.
examples/solid/start-supabase-basic/src/routes/index.tsx (1)
3-13: LGTMRoot route wiring looks good.
| @@ -0,0 +1,20 @@ | |||
| node_modules | |||
| # Keep environment variables out of version control | |||
| !.env | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the .env negation pattern—environment files should not be committed.
Lines 3 and 9 contain contradictory patterns: !.env (negate/allow .env) and .env (ignore .env). Since .env files typically contain secrets (Supabase credentials, API keys, etc.), they must never be committed to version control. The negation pattern !.env on line 3 would override the ignore rule and allow these sensitive files into the repository.
Assuming .env should be ignored, remove line 3 entirely. If environment templates are needed for developers, use a pattern like .env.example instead.
-# Keep environment variables out of version control
-!.env
package-lock.jsonIf you need to provide a template for environment variables, add:
+# Keep environment variables out of version control
+.env
+# Environment template for reference (no secrets)
+# .env.exampleAlso applies to: 9-9
🤖 Prompt for AI Agents
In examples/solid/start-supabase-basic/.gitignore around lines 3 and 9, remove
the negation pattern on line 3 ("!.env") so that the existing ".env" ignore
entry (line 9) is not overridden; delete the "! .env" line, keep or add a ".env"
entry to ensure environment files remain ignored, and if a template is needed
add a separate ".env.example" (or similar) entry instead of un-negating ".env".
| /public/build# Sentry Config File | ||
| .env.sentry-build-plugin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the comment placement on line 15.
The comment # Sentry Config File is appended to the end of /public/build on line 15, which breaks readability and may cause parsing issues. Move the comment to its own line above .env.sentry-build-plugin.
-/public/build# Sentry Config File
+/public/build
+# Sentry Config File
.env.sentry-build-plugin📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /public/build# Sentry Config File | |
| .env.sentry-build-plugin | |
| /public/build | |
| # Sentry Config File | |
| .env.sentry-build-plugin |
🤖 Prompt for AI Agents
In examples/solid/start-supabase-basic/.gitignore around lines 15 to 16, the
comment "# Sentry Config File" is currently appended to the end of the
"/public/build" entry which reduces readability and can confuse parsers; move
the comment onto its own line immediately above the ".env.sentry-build-plugin"
entry so "/public/build" is one line by itself and the Sentry comment sits alone
on the previous line.
| "dev": "vite dev", | ||
| "build": "vite build && tsc --noEmit", | ||
| "start": "vite start" | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the production start script.
Line 10 runs vite start, but Vite doesn’t ship that command; the script fails immediately. Switch to the supported preview command so npm run start works.
Apply this diff:
"scripts": {
"dev": "vite dev",
"build": "vite build && tsc --noEmit",
- "start": "vite start"
+ "start": "vite preview"
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "dev": "vite dev", | |
| "build": "vite build && tsc --noEmit", | |
| "start": "vite start" | |
| }, | |
| "dev": "vite dev", | |
| "build": "vite build && tsc --noEmit", | |
| "start": "vite preview" | |
| }, |
🤖 Prompt for AI Agents
In examples/solid/start-supabase-basic/package.json around lines 8 to 11, the
"start" script incorrectly uses "vite start" which is not a valid Vite command;
replace it with the supported production preview command "vite preview" so npm
run start will run the Vite preview server for built assets.
| "@supabase/ssr": "^0.5.2", | ||
| "@supabase/supabase-js": "^2.48.1", | ||
| "@tanstack/solid-router": "^1.133.25", | ||
| "@tanstack/solid-router-devtools": "^1.133.25", | ||
| "@tanstack/solid-start": "^1.133.26", | ||
| "solid-js": "^1.9.9", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Use workspace ranges for internal TanStack packages.
Lines 16-21 pin internal packages to public versions, ignoring the repo guideline to consume them via the workspace linker. Update these entries to workspace:* so examples test against the local sources.
[As per coding guidelines]
Apply this diff:
"dependencies": {
"@supabase/ssr": "^0.5.2",
"@supabase/supabase-js": "^2.48.1",
- "@tanstack/solid-router": "^1.133.25",
- "@tanstack/solid-router-devtools": "^1.133.25",
- "@tanstack/solid-start": "^1.133.26",
+ "@tanstack/solid-router": "workspace:*",
+ "@tanstack/solid-router-devtools": "workspace:*",
+ "@tanstack/solid-start": "workspace:*",
"solid-js": "^1.9.9",
"redaxios": "^0.5.1"
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "@supabase/ssr": "^0.5.2", | |
| "@supabase/supabase-js": "^2.48.1", | |
| "@tanstack/solid-router": "^1.133.25", | |
| "@tanstack/solid-router-devtools": "^1.133.25", | |
| "@tanstack/solid-start": "^1.133.26", | |
| "solid-js": "^1.9.9", | |
| "@supabase/ssr": "^0.5.2", | |
| "@supabase/supabase-js": "^2.48.1", | |
| "@tanstack/solid-router": "workspace:*", | |
| "@tanstack/solid-router-devtools": "workspace:*", | |
| "@tanstack/solid-start": "workspace:*", | |
| "solid-js": "^1.9.9", |
🤖 Prompt for AI Agents
In examples/solid/start-supabase-basic/package.json around lines 16 to 21, the
internal TanStack packages are pinned to public versions; update the @tanstack/*
entries to use the workspace linker by replacing their version strings with
"workspace:*" (specifically for @tanstack/solid-router,
@tanstack/solid-router-devtools, and @tanstack/solid-start) so the example
consumes local sources per repo guidelines; leave third-party deps (e.g.,
@supabase/*, solid-js) unchanged.
| const loginMutation = useMutation({ | ||
| fn: loginFn, | ||
| onSuccess: async (ctx) => { | ||
| if (!ctx.data?.error) { | ||
| await router.invalidate() | ||
| router.navigate({ to: '/' }) | ||
| return | ||
| } | ||
| }, | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Use useServerFn(loginFn) on the client for consistency and SSR boundaries
Calling a server function from a client component should go through useServerFn to avoid bundling/server boundary pitfalls. You already do this for signupFn. Align loginFn too.
Apply this diff:
- const loginMutation = useMutation({
- fn: loginFn,
+ const loginMutation = useMutation({
+ fn: useServerFn(loginFn),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const loginMutation = useMutation({ | |
| fn: loginFn, | |
| onSuccess: async (ctx) => { | |
| if (!ctx.data?.error) { | |
| await router.invalidate() | |
| router.navigate({ to: '/' }) | |
| return | |
| } | |
| }, | |
| }) | |
| const loginMutation = useMutation({ | |
| fn: useServerFn(loginFn), | |
| onSuccess: async (ctx) => { | |
| if (!ctx.data?.error) { | |
| await router.invalidate() | |
| router.navigate({ to: '/' }) | |
| return | |
| } | |
| }, | |
| }) |
🤖 Prompt for AI Agents
In examples/solid/start-supabase-basic/src/components/Login.tsx around lines 11
to 20, the login mutation calls the server function directly (loginFn) from a
client component; change it to use useServerFn(loginFn) like signup to preserve
SSR boundaries and avoid bundling server code into the client. Replace direct
references to loginFn with the handler returned by useServerFn, pass that
handler into your mutation fn, and ensure any imports/typing reflect useServerFn
usage so the client only calls the server bridge rather than bundling the server
function.
| <HydrationScript /> | ||
| </head> | ||
| <body> | ||
| <HeadContent /> | ||
| <div class="p-2 flex gap-2 text-lg"> | ||
| <Link | ||
| to="/" | ||
| activeProps={{ | ||
| class: 'font-bold', | ||
| }} | ||
| activeOptions={{ exact: true }} | ||
| > | ||
| Home | ||
| </Link>{' '} | ||
| <Link | ||
| to="/posts" | ||
| activeProps={{ | ||
| class: 'font-bold', | ||
| }} | ||
| > | ||
| Posts | ||
| </Link> | ||
| <div class="ml-auto"> | ||
| {user ? ( | ||
| <> | ||
| <span class="mr-2">{user.email}</span> | ||
| <Link to="/logout">Logout</Link> | ||
| </> | ||
| ) : ( | ||
| <Link to="/login">Login</Link> | ||
| )} | ||
| </div> | ||
| </div> | ||
| <hr /> | ||
| {children} | ||
| <TanStackRouterDevtools position="bottom-right" /> | ||
| <Scripts /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move <HeadContent /> into <head>
HeadContent renders the meta/link tags declared across the route tree. Rendering it inside <body> causes those tags (viewport, SEO, icons) to land in the wrong place, so browsers ignore them. Shift it into the <head> alongside the hydration script.
- <head>
- <HydrationScript />
- </head>
- <body>
- <HeadContent />
+ <head>
+ <HydrationScript />
+ <HeadContent />
+ </head>
+ <body>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <HydrationScript /> | |
| </head> | |
| <body> | |
| <HeadContent /> | |
| <div class="p-2 flex gap-2 text-lg"> | |
| <Link | |
| to="/" | |
| activeProps={{ | |
| class: 'font-bold', | |
| }} | |
| activeOptions={{ exact: true }} | |
| > | |
| Home | |
| </Link>{' '} | |
| <Link | |
| to="/posts" | |
| activeProps={{ | |
| class: 'font-bold', | |
| }} | |
| > | |
| Posts | |
| </Link> | |
| <div class="ml-auto"> | |
| {user ? ( | |
| <> | |
| <span class="mr-2">{user.email}</span> | |
| <Link to="/logout">Logout</Link> | |
| </> | |
| ) : ( | |
| <Link to="/login">Login</Link> | |
| )} | |
| </div> | |
| </div> | |
| <hr /> | |
| {children} | |
| <TanStackRouterDevtools position="bottom-right" /> | |
| <Scripts /> | |
| <head> | |
| <HydrationScript /> | |
| <HeadContent /> | |
| </head> | |
| <body> | |
| <div class="p-2 flex gap-2 text-lg"> | |
| <Link | |
| to="/" | |
| activeProps={{ | |
| class: 'font-bold', | |
| }} | |
| activeOptions={{ exact: true }} | |
| > | |
| Home | |
| </Link>{' '} | |
| <Link | |
| to="/posts" | |
| activeProps={{ | |
| class: 'font-bold', | |
| }} | |
| > | |
| Posts | |
| </Link> | |
| <div class="ml-auto"> | |
| {user ? ( | |
| <> | |
| <span class="mr-2">{user.email}</span> | |
| <Link to="/logout">Logout</Link> | |
| </> | |
| ) : ( | |
| <Link to="/login">Login</Link> | |
| )} | |
| </div> | |
| </div> | |
| <hr /> | |
| {children} | |
| <TanStackRouterDevtools position="bottom-right" /> | |
| <Scripts /> |
🤖 Prompt for AI Agents
In examples/solid/start-supabase-basic/src/routes/__root.tsx around lines 103 to
139, HeadContent is currently rendered inside the <body> which causes meta/link
tags to be placed incorrectly; move the <HeadContent /> component into the
<head> section alongside <HydrationScript /> so route-declared meta/link tags
are emitted in the document head; adjust markup order to render <HydrationScript
/> and <HeadContent /> inside the head, keeping the navigation and children in
the body.
| to="/posts/$postId" | ||
| params={{ | ||
| postId: post.id, | ||
| }} | ||
| class="block py-1 text-blue-800 hover:text-blue-600" | ||
| activeProps={{ class: 'text-black font-bold' }} | ||
| > | ||
| <div>{post.title.substring(0, 20)}</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Point the post links at the authed child route
The detail route you defined lives at /_authed/posts/$postId, but these links navigate to /posts/$postId, which doesn’t exist. That yields a 404 instead of loading the loader/component. Please target the authed child (or use a relative ./$postId).
- to="/posts/$postId"
+ to="./$postId"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| to="/posts/$postId" | |
| params={{ | |
| postId: post.id, | |
| }} | |
| class="block py-1 text-blue-800 hover:text-blue-600" | |
| activeProps={{ class: 'text-black font-bold' }} | |
| > | |
| <div>{post.title.substring(0, 20)}</div> | |
| to="./$postId" | |
| params={{ | |
| postId: post.id, | |
| }} | |
| class="block py-1 text-blue-800 hover:text-blue-600" | |
| activeProps={{ class: 'text-black font-bold' }} | |
| > | |
| <div>{post.title.substring(0, 20)}</div> |
🤖 Prompt for AI Agents
In examples/solid/start-supabase-basic/src/routes/_authed/posts.tsx around lines
20 to 27, the Link currently points to /posts/$postId which is the wrong route
and causes 404s; update the link target to the authenticated child route (either
change to '/_authed/posts/$postId' or use a relative path './$postId') so the
loader/component for the authed route is invoked; ensure params/postId usage
remains unchanged and adjust any routing helpers to use the new path.
| if (error) { | ||
| return { | ||
| error: true, | ||
| message: error.message, | ||
| } | ||
| } | ||
|
|
||
| throw redirect({ | ||
| href: '/', | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle logout errors with a throw or redirect; don't return data
Returning an object here with no component means a blank page on error. Throw to hit an error boundary or redirect with a query param.
if (error) {
- return {
- error: true,
- message: error.message,
- }
+ // Option A: Throw to error boundary
+ throw new Error(`Logout failed: ${error.message}`)
+ // Option B: Or redirect with context:
+ // throw redirect({ href: '/?logout_error=1' })
}Additionally, consider adding an errorComponent to the route:
export const Route = createFileRoute('/logout')({
preload: false,
loader: () => logoutFn(),
+ errorComponent: ({ error }) => (
+ <div class="p-2 text-red-500">Logout failed: {String(error?.message || 'Unknown error')}</div>
+ ),
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (error) { | |
| return { | |
| error: true, | |
| message: error.message, | |
| } | |
| } | |
| throw redirect({ | |
| href: '/', | |
| }) | |
| }) | |
| if (error) { | |
| // Option A: Throw to error boundary | |
| throw new Error(`Logout failed: ${error.message}`) | |
| // Option B: Or redirect with context: | |
| // throw redirect({ href: '/?logout_error=1' }) | |
| } | |
| throw redirect({ | |
| href: '/', | |
| }) | |
| }) |
🤖 Prompt for AI Agents
In examples/solid/start-supabase-basic/src/routes/logout.tsx around lines 9 to
19, the route currently returns an object on error which renders a blank page;
instead either throw the error to trigger an error boundary or perform a
redirect (e.g., to '/' with an error query param) so the user is redirected on
failure. Update the error branch to throw the Error (or call redirect with an
error query) rather than returning an object, and also add an errorComponent for
this route so thrown errors are handled and displayed to the user.
| export const fetchPost = createServerFn({ method: 'GET' }) | ||
| .inputValidator((d: string) => d) | ||
| .handler(async ({ data: postId }) => { | ||
| console.info(`Fetching post with id ${postId}...`) | ||
| const post = await axios | ||
| .get<PostType>(`https://jsonplaceholder.typicode.com/posts/${postId}`) | ||
| .then((r) => r.data) | ||
| .catch((err) => { | ||
| console.error(err) | ||
| if (err.status === 404) { | ||
| throw notFound() | ||
| } | ||
| throw err | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure 404 detection handles redaxios errors.
Lines 18-23 assume err.status, but redaxios (and axios) put the code on err.response.status. As written, a 404 never maps to notFound(), so loaders fall back to the generic error boundary instead of the intended 404 UI. Extract the status from err.response before branching.
Apply this diff:
- .catch((err) => {
- console.error(err)
- if (err.status === 404) {
- throw notFound()
- }
- throw err
- })
+ .catch((err) => {
+ console.error(err)
+ const status =
+ typeof err === 'object' && err !== null && 'response' in err
+ ? (err as { response?: { status?: number } }).response?.status
+ : undefined
+ if (status === 404) {
+ throw notFound()
+ }
+ throw err
+ })🤖 Prompt for AI Agents
In examples/solid/start-supabase-basic/src/utils/posts.ts around lines 11 to 24,
the error handler checks err.status which is undefined for redaxios/axios
errors; extract the HTTP status from err.response (e.g. const status =
err?.response?.status ?? err.status) and use that status to detect 404 and call
notFound(); also preserve existing error logging and rethrow the original error
when not a 404.
| cookies.forEach((cookie) => { | ||
| setCookie(cookie.name, cookie.value) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Preserve Supabase cookie attributes when relaying cookies
Supabase’s setAll passes cookie.options (httpOnly, secure, maxAge, path, etc.). Dropping them means session cookies lose their protections and won't persist or clear correctly, so authentication becomes unreliable and far less secure. Please forward the options when calling Solid Start’s setCookie.
- cookies.forEach((cookie) => {
- setCookie(cookie.name, cookie.value)
- })
+ cookies.forEach((cookie) => {
+ setCookie(cookie.name, cookie.value, cookie.options)
+ })📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| cookies.forEach((cookie) => { | |
| setCookie(cookie.name, cookie.value) | |
| }) | |
| cookies.forEach((cookie) => { | |
| setCookie(cookie.name, cookie.value, cookie.options) | |
| }) |
🤖 Prompt for AI Agents
In examples/solid/start-supabase-basic/src/utils/supabase.ts around lines 17 to
19, the code iterates cookies and calls setCookie(cookie.name, cookie.value) but
drops cookie.options (httpOnly, secure, maxAge, path, sameSite, etc.), so
session cookies lose attributes; update the call to forward the original
options: setCookie(cookie.name, cookie.value, cookie.options) (or map
cookie.options to the Solid setCookie option shape if needed), preserving all
relevant attributes so cookies persist, clear and remain secure.
|
Thanks! It would be more robust to use the tailwind postcss file instead of their new vite plugin for now. It's still tw 4 - see: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, it should be added to the list of examples in the docs. It's the docs/framework/start/config.json
|
View your CI Pipeline Execution ↗ for commit 03fe120
☁️ Nx Cloud last updated this comment at |
More templates
@tanstack/arktype-adapter
@tanstack/directive-functions-plugin
@tanstack/eslint-plugin-router
@tanstack/history
@tanstack/nitro-v2-vite-plugin
@tanstack/react-router
@tanstack/react-router-devtools
@tanstack/react-router-ssr-query
@tanstack/react-start
@tanstack/react-start-client
@tanstack/react-start-server
@tanstack/router-cli
@tanstack/router-core
@tanstack/router-devtools
@tanstack/router-devtools-core
@tanstack/router-generator
@tanstack/router-plugin
@tanstack/router-ssr-query-core
@tanstack/router-utils
@tanstack/router-vite-plugin
@tanstack/server-functions-plugin
@tanstack/solid-router
@tanstack/solid-router-devtools
@tanstack/solid-router-ssr-query
@tanstack/solid-start
@tanstack/solid-start-client
@tanstack/solid-start-server
@tanstack/start-client-core
@tanstack/start-plugin-core
@tanstack/start-server-core
@tanstack/start-static-server-functions
@tanstack/start-storage-context
@tanstack/valibot-adapter
@tanstack/virtual-file-routes
@tanstack/zod-adapter
commit: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
♻️ Duplicate comments (8)
examples/solid/start-basic-supabase/.gitignore (2)
3-3: Remove the.envnegation pattern—environment files should not be committed.The negation pattern
!.envon line 3 overrides the ignore rule on line 9, allowing sensitive environment files containing Supabase credentials and API keys to be committed to version control.
15-15: Fix the comment placement.The comment
# Sentry Config Fileis appended to the end of/public/build, which breaks readability. Move it to its own line above.env.sentry-build-plugin.examples/solid/start-basic-supabase/src/utils/supabase.ts (1)
17-19: Preserve Supabase cookie attributes when relaying cookies.The current implementation drops
cookie.options(httpOnly, secure, maxAge, path, sameSite, etc.), which compromises session security and cookie persistence. Forward the options parameter tosetCookie.examples/solid/start-basic-supabase/src/routes/logout.tsx (1)
9-14: Handle logout errors with a throw or redirect; don't return dataReturning an error object from the loader without a component renders a blank page. Either throw an error to trigger the error boundary, or redirect to a route with error context (e.g.,
/?logout_error=1).Apply this diff to throw instead:
if (error) { - return { - error: true, - message: error.message, - } + throw new Error(`Logout failed: ${error.message}`) }Alternatively, redirect with error context:
if (error) { - return { - error: true, - message: error.message, - } + throw redirect({ href: `/?logout_error=${encodeURIComponent(error.message)}` }) }examples/solid/start-basic-supabase/src/components/Login.tsx (1)
11-20: Use useServerFn(loginFn) for consistency and proper SSR boundariesThe login mutation calls
loginFndirectly while the signup mutation correctly wrapssignupFnwithuseServerFn(line 23). This inconsistency can lead to SSR/bundling issues where server code is inadvertently bundled into the client.Apply this diff:
const loginMutation = useMutation({ - fn: loginFn, + fn: useServerFn(loginFn), onSuccess: async (ctx) => {examples/solid/start-basic-supabase/src/routes/_authed/posts.tsx (1)
20-27: Fix link target to the authed child route.The detail route lives at
/_authed/posts/$postId, but this Link navigates to/posts/$postId(404). Use a relative child route or the full authed path.Apply this diff:
- <Link - to="/posts/$postId" + <Link + to="./$postId"examples/solid/start-basic-supabase/src/utils/posts.ts (1)
18-24: Handle redaxios/axios errors viaerr.response.status.
err.statusis undefined; 404s won’t map tonotFound().- .catch((err) => { - console.error(err) - if (err.status === 404) { - throw notFound() - } - throw err - }) + .catch((err) => { + console.error(err) + const status = + typeof err === 'object' && err !== null && 'response' in err + ? (err as { response?: { status?: number } }).response?.status + : undefined + if (status === 404) { + throw notFound() + } + throw err + })examples/solid/start-basic-supabase/src/hooks/useMutation.ts (1)
15-31: Clear stale data/error on new submissions and on failures.On re-submit, previous
data/errorlinger; on catch,dataisn’t cleared. Set and order state to avoid flicker and stale UI. This mirrors an earlier suggestion.const mutate = async (variables: TVariables): Promise<TData | undefined> => { - setStatus('pending') + setStatus('pending') setSubmittedAt(Date.now()) setVariables(() => variables) + setError(undefined) + setData(undefined) try { - const result = await opts.fn(variables) - await opts.onSuccess?.({ data: result }) + const result = await opts.fn(variables) + // expose result before onSuccess in case onSuccess throws + setData(() => result) + await opts.onSuccess?.({ data: result }) setStatus('success') - setError(undefined) - setData(() => result) return result } catch (err) { setStatus('error') setError(() => err as TError) + setData(undefined) return undefined } }
🧹 Nitpick comments (7)
examples/solid/start-basic-supabase/src/routes/_authed/posts.$postId.tsx (1)
15-17: Remove redundant PostErrorComponent exportThe
PostErrorComponentsimply forwards toErrorComponent, which is already directly assigned as the route'serrorComponent(line 8). This export appears unused and adds unnecessary code.Apply this diff to remove the redundant export:
-export function PostErrorComponent({ error }: ErrorComponentProps) { - return <ErrorComponent error={error} /> -} - function PostComponent() {examples/solid/start-basic-supabase/src/routes/_authed/posts.tsx (1)
15-16: Clarify the demo 404 item or gate it behind dev.Including a fake "Non-existent Post" is fine to showcase
notFound, but it can confuse users. Either remove it, add a comment explaining it, or guard behind a dev flag.As per coding guidelines
Also applies to: 18-31
examples/solid/start-basic-supabase/src/utils/seo.ts (1)
1-33: Usepropertyfor Open Graph tags and skip meta with undefined content.Current code may emit
<meta content="undefined">and OG scrapers expectproperty="og:*". Build tags conditionally and switch OG toproperty.As per coding guidelines
export const seo = ({ title, description, keywords, image, }: { title: string description?: string image?: string keywords?: string }) => { - const tags = [ - { title }, - { name: 'description', content: description }, - { name: 'keywords', content: keywords }, - { name: 'twitter:title', content: title }, - { name: 'twitter:description', content: description }, - { name: 'twitter:creator', content: '@tannerlinsley' }, - { name: 'twitter:site', content: '@tannerlinsley' }, - { name: 'og:type', content: 'website' }, - { name: 'og:title', content: title }, - { name: 'og:description', content: description }, - ...(image - ? [ - { name: 'twitter:image', content: image }, - { name: 'twitter:card', content: 'summary_large_image' }, - { name: 'og:image', content: image }, - ] - : []), - ] + const tags = [ + { title }, + ...(description ? [{ name: 'description', content: description }] : []), + ...(keywords ? [{ name: 'keywords', content: keywords }] : []), + { name: 'twitter:title', content: title }, + ...(description ? [{ name: 'twitter:description', content: description }] : []), + { name: 'twitter:creator', content: '@tannerlinsley' }, + { name: 'twitter:site', content: '@tannerlinsley' }, + { property: 'og:type', content: 'website' }, + { property: 'og:title', content: title }, + ...(description ? [{ property: 'og:description', content: description }] : []), + ...(image + ? [ + { name: 'twitter:image', content: image }, + { name: 'twitter:card', content: 'summary_large_image' }, + { property: 'og:image', content: image }, + ] + : []), + ] return tags }examples/solid/start-basic-supabase/src/routes/_authed.tsx (1)
6-21: Avoid a circular dependency between route and Login by movingloginFnto a server utils module.
_authed.tsximportsLogin, andLoginappears to useloginFn. This can create an ESM cycle and undefined imports at runtime. ExtractloginFn(andsignupFn) tosrc/server/auth.ts(orutils/authFns.ts) and import from there in both places. Also consider returning a consistent success shape (e.g.,{ error: false }) for simpler client checks.As per coding guidelines
- export const loginFn = createServerFn({ method: 'POST' }) + // src/server/auth.ts + export const loginFn = createServerFn({ method: 'POST' }) .inputValidator((d: { email: string; password: string }) => d) .handler(async ({ data }) => { const supabase = getSupabaseServerClient() const { error } = await supabase.auth.signInWithPassword({ email: data.email, password: data.password, }) if (error) { return { error: true, message: error.message, } } + return { error: false } })Also applies to: 29-35
examples/solid/start-basic-supabase/src/routes/__root.tsx (1)
97-107: Optional: import JSX type directly and place HeadContent in .Use
import type { JSX } from 'solid-js'and consider moving<HeadContent />into<head>for semantics (framework may already handle this).-import type * as Solid from 'solid-js' +import type { JSX } from 'solid-js' ... -function RootDocument({ children }: { children: Solid.JSX.Element }) { +function RootDocument({ children }: { children: JSX.Element }) { @@ - <head> - <HydrationScript /> - </head> - <body> - <HeadContent /> + <head> + <HydrationScript /> + <HeadContent /> + </head> + <body>examples/solid/start-basic-supabase/src/routes/signup.tsx (2)
8-11: Add runtime input validation (avoid passthrough).The validator currently trusts client input. Validate shape/types server-side to prevent undefined/null values and coercion bugs.
Apply this minimal, no-deps diff:
-export const signupFn = createServerFn({ method: 'POST' }) - .inputValidator( - (d: { email: string; password: string; redirectUrl?: string }) => d, - ) +export const signupFn = createServerFn({ method: 'POST' }) + .inputValidator((d: unknown) => { + const obj = d as Record<string, unknown> + const email = typeof obj?.email === 'string' ? obj.email.trim() : '' + const password = typeof obj?.password === 'string' ? obj.password : '' + const redirectUrl = + typeof obj?.redirectUrl === 'string' ? obj.redirectUrl : undefined + if (!email || !password) { + throw new Error('Invalid input') + } + return { email, password, redirectUrl } + })
24-28: <!-- [scratchpad_start]
[task verify_security_concern_3/3 confirm_usage_1/1 assess_vulnerability_1/1][observations]
- redirect.ts: redirect() function accepts href directly, no validation - just sets Location header
- resolveRedirect (router.ts 2321-2330): DOES validate!
- Strips same-origin URLs: https://foo.com/bar → /bar
- Preserves cross-origin URLs as-is (vulnerability vector)
- signup.tsx code flow: Auth component's onSubmit only sends { email, password }
- redirectUrl parameter exists in inputValidator but is NEVER PASSED from component
- Auth.tsx form has no redirectUrl field, only email/password
- No search param extraction visible in examples/solid/start-basic-supabase/src/
[analysis]
- redirectUrl is currently UNUSED in this example - defensive code for feature not implemented
- IF redirectUrl were to be passed (e.g., from search params), it COULD be vulnerable to cross-origin redirects
- resolveRedirect provides protection for same-origin but NOT cross-origin
- Learning confirms router only strips origin for same-origin URLs
- Suggested fix (checking for '/') is good defensive coding, aligns with best practice
- However, current code has no actual vulnerability since parameter isn't used
[pending]
- None - enough evidence gathered
[done]
- Verified redirect implementation: router DOES have some protection but not for cross-origin
- Confirmed redirectUrl parameter is unused in current component
- Assessment: Review comment identifies potential/defensive concern, fix is reasonable but unnecessary for current code
</scratchpad_end] -->This is defensive code for an unused parameter; redirectUrl is never actually passed from the component.
The Auth component's
onSubmitonly sendspassword— theredirectUrlparameter is accepted in the validator but never populated. Since the parameter currently goes unused, there's no active open redirect vulnerability at this point.However, if this parameter were to be implemented in the future (e.g., extracting from search params), the suggested validation would be prudent. The router's
resolveRedirectalready strips same-origin URLs but preserves cross-origin ones, making cross-origin redirects a potential concern. The fix defensively restricts to relative paths starting with/, which is a safe pattern.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (26)
docs/start/config.json(1 hunks)examples/solid/start-basic-supabase/.gitignore(1 hunks)examples/solid/start-basic-supabase/.prettierignore(1 hunks)examples/solid/start-basic-supabase/package.json(1 hunks)examples/solid/start-basic-supabase/src/components/Auth.tsx(1 hunks)examples/solid/start-basic-supabase/src/components/DefaultCatchBoundary.tsx(1 hunks)examples/solid/start-basic-supabase/src/components/Login.tsx(1 hunks)examples/solid/start-basic-supabase/src/components/NotFound.tsx(1 hunks)examples/solid/start-basic-supabase/src/hooks/useMutation.ts(1 hunks)examples/solid/start-basic-supabase/src/routeTree.gen.ts(1 hunks)examples/solid/start-basic-supabase/src/router.tsx(1 hunks)examples/solid/start-basic-supabase/src/routes/__root.tsx(1 hunks)examples/solid/start-basic-supabase/src/routes/_authed.tsx(1 hunks)examples/solid/start-basic-supabase/src/routes/_authed/posts.$postId.tsx(1 hunks)examples/solid/start-basic-supabase/src/routes/_authed/posts.index.tsx(1 hunks)examples/solid/start-basic-supabase/src/routes/_authed/posts.tsx(1 hunks)examples/solid/start-basic-supabase/src/routes/index.tsx(1 hunks)examples/solid/start-basic-supabase/src/routes/login.tsx(1 hunks)examples/solid/start-basic-supabase/src/routes/logout.tsx(1 hunks)examples/solid/start-basic-supabase/src/routes/signup.tsx(1 hunks)examples/solid/start-basic-supabase/src/styles/app.css(1 hunks)examples/solid/start-basic-supabase/src/utils/posts.ts(1 hunks)examples/solid/start-basic-supabase/src/utils/seo.ts(1 hunks)examples/solid/start-basic-supabase/src/utils/supabase.ts(1 hunks)examples/solid/start-basic-supabase/tsconfig.json(1 hunks)examples/solid/start-basic-supabase/vite.config.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- examples/solid/start-basic-supabase/.prettierignore
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use TypeScript in strict mode with extensive type safety across the codebase
Files:
examples/solid/start-basic-supabase/src/routes/_authed/posts.index.tsxexamples/solid/start-basic-supabase/src/routes/logout.tsxexamples/solid/start-basic-supabase/src/components/DefaultCatchBoundary.tsxexamples/solid/start-basic-supabase/src/utils/supabase.tsexamples/solid/start-basic-supabase/src/components/NotFound.tsxexamples/solid/start-basic-supabase/vite.config.tsexamples/solid/start-basic-supabase/src/routes/index.tsxexamples/solid/start-basic-supabase/src/routes/_authed/posts.tsxexamples/solid/start-basic-supabase/src/components/Auth.tsxexamples/solid/start-basic-supabase/src/router.tsxexamples/solid/start-basic-supabase/src/routes/_authed/posts.$postId.tsxexamples/solid/start-basic-supabase/src/routes/_authed.tsxexamples/solid/start-basic-supabase/src/hooks/useMutation.tsexamples/solid/start-basic-supabase/src/routeTree.gen.tsexamples/solid/start-basic-supabase/src/routes/login.tsxexamples/solid/start-basic-supabase/src/routes/signup.tsxexamples/solid/start-basic-supabase/src/routes/__root.tsxexamples/solid/start-basic-supabase/src/utils/posts.tsexamples/solid/start-basic-supabase/src/components/Login.tsxexamples/solid/start-basic-supabase/src/utils/seo.ts
**/src/routes/**
📄 CodeRabbit inference engine (AGENTS.md)
Place file-based routes under src/routes/ directories
Files:
examples/solid/start-basic-supabase/src/routes/_authed/posts.index.tsxexamples/solid/start-basic-supabase/src/routes/logout.tsxexamples/solid/start-basic-supabase/src/routes/index.tsxexamples/solid/start-basic-supabase/src/routes/_authed/posts.tsxexamples/solid/start-basic-supabase/src/routes/_authed/posts.$postId.tsxexamples/solid/start-basic-supabase/src/routes/_authed.tsxexamples/solid/start-basic-supabase/src/routes/login.tsxexamples/solid/start-basic-supabase/src/routes/signup.tsxexamples/solid/start-basic-supabase/src/routes/__root.tsx
examples/{react,solid}/**
📄 CodeRabbit inference engine (AGENTS.md)
Keep example applications under examples/react/ and examples/solid/
Files:
examples/solid/start-basic-supabase/src/routes/_authed/posts.index.tsxexamples/solid/start-basic-supabase/src/routes/logout.tsxexamples/solid/start-basic-supabase/src/components/DefaultCatchBoundary.tsxexamples/solid/start-basic-supabase/tsconfig.jsonexamples/solid/start-basic-supabase/src/utils/supabase.tsexamples/solid/start-basic-supabase/src/components/NotFound.tsxexamples/solid/start-basic-supabase/vite.config.tsexamples/solid/start-basic-supabase/src/routes/index.tsxexamples/solid/start-basic-supabase/src/routes/_authed/posts.tsxexamples/solid/start-basic-supabase/src/components/Auth.tsxexamples/solid/start-basic-supabase/src/router.tsxexamples/solid/start-basic-supabase/src/routes/_authed/posts.$postId.tsxexamples/solid/start-basic-supabase/src/routes/_authed.tsxexamples/solid/start-basic-supabase/src/hooks/useMutation.tsexamples/solid/start-basic-supabase/src/routeTree.gen.tsexamples/solid/start-basic-supabase/src/routes/login.tsxexamples/solid/start-basic-supabase/src/routes/signup.tsxexamples/solid/start-basic-supabase/src/routes/__root.tsxexamples/solid/start-basic-supabase/src/utils/posts.tsexamples/solid/start-basic-supabase/src/components/Login.tsxexamples/solid/start-basic-supabase/package.jsonexamples/solid/start-basic-supabase/src/utils/seo.tsexamples/solid/start-basic-supabase/src/styles/app.css
docs/{router,start}/**
📄 CodeRabbit inference engine (AGENTS.md)
Place router docs under docs/router/ and start framework docs under docs/start/
Files:
docs/start/config.json
**/package.json
📄 CodeRabbit inference engine (AGENTS.md)
Use workspace:* protocol for internal dependencies in package.json files
Files:
examples/solid/start-basic-supabase/package.json
🧠 Learnings (3)
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript in strict mode with extensive type safety across the codebase
Applied to files:
examples/solid/start-basic-supabase/tsconfig.json
📚 Learning: 2025-10-01T18:31:35.420Z
Learnt from: schiller-manuel
PR: TanStack/router#5330
File: e2e/react-start/custom-basepath/src/routeTree.gen.ts:58-61
Timestamp: 2025-10-01T18:31:35.420Z
Learning: Do not review files named `routeTree.gen.ts` in TanStack Router repositories, as these are autogenerated files that should not be manually modified.
Applied to files:
examples/solid/start-basic-supabase/src/routeTree.gen.ts
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to **/package.json : Use workspace:* protocol for internal dependencies in package.json files
Applied to files:
examples/solid/start-basic-supabase/package.json
🧬 Code graph analysis (14)
examples/solid/start-basic-supabase/src/routes/_authed/posts.index.tsx (3)
examples/solid/start-basic-supabase/src/routes/_authed.tsx (1)
Route(23-35)examples/solid/start-basic-supabase/src/routes/_authed/posts.$postId.tsx (1)
Route(6-13)examples/solid/start-basic-supabase/src/routes/_authed/posts.tsx (1)
Route(4-7)
examples/solid/start-basic-supabase/src/routes/logout.tsx (3)
examples/solid/start-basic-supabase/src/utils/supabase.ts (1)
getSupabaseServerClient(4-24)examples/solid/start-basic-supabase/src/routes/_authed.tsx (1)
Route(23-35)examples/solid/start-basic-supabase/src/routes/index.tsx (1)
Route(3-5)
examples/solid/start-basic-supabase/src/components/DefaultCatchBoundary.tsx (4)
e2e/solid-start/server-routes/src/components/DefaultCatchBoundary.tsx (1)
DefaultCatchBoundary(10-53)examples/solid/start-basic-solid-query/src/components/DefaultCatchBoundary.tsx (1)
DefaultCatchBoundary(10-53)examples/solid/start-basic-cloudflare/src/components/DefaultCatchBoundary.tsx (1)
DefaultCatchBoundary(10-53)examples/solid/start-basic/src/components/DefaultCatchBoundary.tsx (1)
DefaultCatchBoundary(10-53)
examples/solid/start-basic-supabase/src/utils/supabase.ts (1)
packages/start-server-core/src/request-response.ts (2)
getCookies(213-216)setCookie(239-246)
examples/solid/start-basic-supabase/src/components/NotFound.tsx (5)
examples/solid/start-basic-solid-query/src/components/NotFound.tsx (1)
NotFound(3-25)examples/solid/start-basic-cloudflare/src/components/NotFound.tsx (1)
NotFound(3-25)examples/solid/start-basic/src/components/NotFound.tsx (1)
NotFound(3-25)e2e/solid-start/server-routes/src/components/NotFound.tsx (1)
NotFound(3-25)examples/solid/start-basic-nitro/src/components/NotFound.tsx (1)
NotFound(3-25)
examples/solid/start-basic-supabase/src/routes/index.tsx (1)
examples/solid/start-basic-supabase/src/routes/__root.tsx (1)
Route(32-87)
examples/solid/start-basic-supabase/src/routes/_authed/posts.tsx (4)
examples/solid/start-basic-supabase/src/routes/_authed.tsx (1)
Route(23-35)examples/solid/start-basic-supabase/src/routes/_authed/posts.$postId.tsx (1)
Route(6-13)examples/solid/start-basic-supabase/src/routes/_authed/posts.index.tsx (1)
Route(3-5)examples/solid/start-basic-supabase/src/utils/posts.ts (1)
fetchPosts(29-37)
examples/solid/start-basic-supabase/src/routes/_authed/posts.$postId.tsx (5)
examples/solid/start-basic-supabase/src/routes/_authed.tsx (1)
Route(23-35)examples/solid/start-basic-supabase/src/routes/_authed/posts.index.tsx (1)
Route(3-5)examples/solid/start-basic-supabase/src/routes/_authed/posts.tsx (1)
Route(4-7)examples/solid/start-basic-supabase/src/utils/posts.ts (1)
fetchPost(11-27)examples/solid/start-basic-supabase/src/components/NotFound.tsx (1)
NotFound(4-26)
examples/solid/start-basic-supabase/src/routes/_authed.tsx (3)
examples/solid/start-basic-supabase/src/utils/supabase.ts (1)
getSupabaseServerClient(4-24)examples/solid/start-basic-supabase/src/routes/__root.tsx (1)
Route(32-87)examples/solid/start-basic-supabase/src/components/Login.tsx (1)
Login(8-72)
examples/solid/start-basic-supabase/src/routeTree.gen.ts (2)
e2e/react-router/js-only-file-based/src/routeTree.gen.js (1)
IndexRoute(30-34)examples/solid/start-basic-supabase/src/router.tsx (1)
getRouter(4-11)
examples/solid/start-basic-supabase/src/routes/login.tsx (1)
examples/solid/start-basic-supabase/src/components/Login.tsx (1)
Login(8-72)
examples/solid/start-basic-supabase/src/routes/signup.tsx (3)
examples/solid/start-basic-supabase/src/utils/supabase.ts (1)
getSupabaseServerClient(4-24)examples/solid/start-basic-supabase/src/hooks/useMutation.ts (1)
useMutation(3-42)examples/solid/start-basic-supabase/src/components/Auth.tsx (1)
Auth(3-54)
examples/solid/start-basic-supabase/src/routes/__root.tsx (5)
examples/solid/start-basic-supabase/src/utils/supabase.ts (1)
getSupabaseServerClient(4-24)examples/solid/start-basic-supabase/src/routes/_authed.tsx (1)
Route(23-35)examples/solid/start-basic-supabase/src/utils/seo.ts (1)
seo(1-33)examples/solid/start-basic-supabase/src/components/DefaultCatchBoundary.tsx (1)
DefaultCatchBoundary(10-53)examples/solid/start-basic-supabase/src/components/NotFound.tsx (1)
NotFound(4-26)
examples/solid/start-basic-supabase/src/components/Login.tsx (4)
examples/solid/start-basic-supabase/src/hooks/useMutation.ts (1)
useMutation(3-42)examples/solid/start-basic-supabase/src/routes/_authed.tsx (1)
loginFn(6-21)examples/solid/start-basic-supabase/src/routes/signup.tsx (1)
signupFn(7-28)examples/solid/start-basic-supabase/src/components/Auth.tsx (1)
Auth(3-54)
🔇 Additional comments (17)
docs/start/config.json (1)
329-332: LGTM!The new "Basic + Supabase" example entry is properly formatted and correctly positioned within the Solid framework examples section.
examples/solid/start-basic-supabase/src/routes/login.tsx (1)
1-10: LGTM!The login route follows standard Solid Router file-based routing patterns and correctly imports and renders the Login component.
examples/solid/start-basic-supabase/src/components/NotFound.tsx (1)
1-26: LGTM!The NotFound component follows the established pattern used across other Solid examples in the repository, with proper TypeScript typing and consistent UI implementation.
examples/solid/start-basic-supabase/src/routes/index.tsx (1)
1-13: LGTM!The index route follows standard Solid Router patterns with a clean, minimal implementation.
examples/solid/start-basic-supabase/src/styles/app.css (2)
1-1: Consider using the Tailwind PostCSS file instead of the new Vite plugin.Per reviewer feedback in the PR comments, the new Vite Tailwind plugin may have robustness and compatibility concerns with Tailwind v4. The PostCSS approach is recommended for better stability.
Based on learnings
3-20: LGTM!The Tailwind base layer customizations are well-structured, providing proper dark mode support and standard utility enhancements.
examples/solid/start-basic-supabase/src/routes/_authed/posts.index.tsx (1)
1-9: LGTM!The posts index route follows standard conventions for authenticated routes and provides a clear placeholder for the posts listing view.
examples/solid/start-basic-supabase/vite.config.ts (1)
1-17: LGTM!The Vite configuration is correct and follows the standard pattern for Solid Start projects with TypeScript path mapping.
examples/solid/start-basic-supabase/src/router.tsx (1)
4-11: LGTM!The router setup follows the standard pattern for Solid Router with proper configuration and type augmentation.
examples/solid/start-basic-supabase/src/routes/_authed/posts.$postId.tsx (2)
6-13: LGTM!The route configuration correctly wires up the loader, error handling, and not-found scenarios for individual post pages.
19-28: LGTM!The component correctly accesses the loader data via
Route.useLoaderData()and renders the post content using Solid's reactive signal pattern.examples/solid/start-basic-supabase/tsconfig.json (1)
1-23: LGTM!The TypeScript configuration correctly enables strict mode and is properly configured for Solid.js with appropriate compiler options and path aliases.
Based on learnings.
examples/solid/start-basic-supabase/src/components/DefaultCatchBoundary.tsx (1)
10-53: LGTM!The error boundary implementation correctly follows the established pattern used in other Solid Start examples, with appropriate error logging and navigation options.
examples/solid/start-basic-supabase/src/components/Auth.tsx (1)
3-53: Form structure and accessibility look goodThe Auth component correctly implements form submission handling with proper preventDefault behavior and disabled state management during pending operations.
examples/solid/start-basic-supabase/src/routes/__root.tsx (1)
125-134: Routes/loginand/logoutexist—no action required.Both routes are properly defined in the codebase (
src/routes/login.tsxandsrc/routes/logout.tsx), so the navbar links are valid.examples/solid/start-basic-supabase/src/routes/_authed.tsx (1)
23-35: Circular dependency and dual login routes confirmed—review architectural pattern.Verification revealed:
_authed.tsxexportsloginFn(server function at line 6)Login.tsximportsloginFnfrom_authedand uses it_authed.tsxrenders<Login />component inerrorComponent- Standalone
/loginroute exists atsrc/routes/login.tsxThis creates a circular dependency and two separate login flows. Confirm whether this dual-route pattern and circular import are intentional, or refactor to have a single login entry point (either redirect to
/loginor consolidate login logic).examples/solid/start-basic-supabase/src/routeTree.gen.ts (1)
1-225: Autogenerated file — skipping review.
routeTree.gen.tsis generated by TanStack Router and should not be edited or reviewed. Exclude from lint/format.Based on learnings
| "scripts": { | ||
| "dev": "vite dev", | ||
| "build": "vite build && tsc --noEmit", | ||
| "start": "vite start" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the production start script.
Line 10 runs vite start, but Vite doesn't ship that command; the script fails immediately. Switch to the supported preview command so npm run start works.
Apply this diff:
"scripts": {
"dev": "vite dev",
"build": "vite build && tsc --noEmit",
- "start": "vite start"
+ "start": "vite preview"
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "start": "vite start" | |
| "start": "vite preview" |
🤖 Prompt for AI Agents
In examples/solid/start-basic-supabase/package.json around line 10, the "start"
script incorrectly uses "vite start" which is not a valid Vite command; update
the script value to use the supported preview command by replacing "vite start"
with "vite preview" so that npm run start works in production.
| "@tanstack/solid-router": "^1.133.25", | ||
| "@tanstack/solid-router-devtools": "^1.133.25", | ||
| "@tanstack/solid-start": "^1.133.26", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use workspace ranges for internal TanStack packages.
Lines 18–20 pin internal packages to public versions, ignoring the repo guideline to consume them via the workspace linker. Update these entries to workspace:* so examples test against the local sources.
Apply this diff:
"dependencies": {
"@supabase/ssr": "^0.5.2",
"@supabase/supabase-js": "^2.48.1",
- "@tanstack/solid-router": "^1.133.25",
- "@tanstack/solid-router-devtools": "^1.133.25",
- "@tanstack/solid-start": "^1.133.26",
+ "@tanstack/solid-router": "workspace:*",
+ "@tanstack/solid-router-devtools": "workspace:*",
+ "@tanstack/solid-start": "workspace:*",
"solid-js": "^1.9.9",
"redaxios": "^0.5.1"
},As per coding guidelines.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "@tanstack/solid-router": "^1.133.25", | |
| "@tanstack/solid-router-devtools": "^1.133.25", | |
| "@tanstack/solid-start": "^1.133.26", | |
| "@tanstack/solid-router": "workspace:*", | |
| "@tanstack/solid-router-devtools": "workspace:*", | |
| "@tanstack/solid-start": "workspace:*", |
🤖 Prompt for AI Agents
In examples/solid/start-basic-supabase/package.json around lines 18 to 20, the
three internal TanStack dependencies are pinned to public versions; change each
of "@tanstack/solid-router", "@tanstack/solid-router-devtools", and
"@tanstack/solid-start" to use the workspace range (workspace:*) so the example
consumes local workspace packages instead of the published versions, then run
npm/yarn install to update the lockfile.
| <label for="email" class="block text-xs"> | ||
| Username | ||
| </label> | ||
| <input | ||
| type="email" | ||
| name="email" | ||
| id="email" | ||
| class="px-2 py-1 w-full rounded border border-gray-500/20 bg-white dark:bg-gray-800" | ||
| /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix label text to match input type
The label displays "Username" but the input has type="email", creating a mismatch between the label and the expected input format.
Apply this diff:
<label for="email" class="block text-xs">
- Username
+ Email
</label>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <label for="email" class="block text-xs"> | |
| Username | |
| </label> | |
| <input | |
| type="email" | |
| name="email" | |
| id="email" | |
| class="px-2 py-1 w-full rounded border border-gray-500/20 bg-white dark:bg-gray-800" | |
| /> | |
| <label for="email" class="block text-xs"> | |
| </label> | |
| <input | |
| type="email" | |
| name="email" | |
| id="email" | |
| class="px-2 py-1 w-full rounded border border-gray-500/20 bg-white dark:bg-gray-800" | |
| /> |
🤖 Prompt for AI Agents
In examples/solid/start-basic-supabase/src/components/Auth.tsx around lines 21
to 29, the label reads "Username" while the input is type="email"; update the
label text to "Email" so it matches the input type and intent (ensure the
label's for attribute remains "email" to preserve accessibility).
| links: [ | ||
| { rel: 'stylesheet', href: appCss }, | ||
| { | ||
| rel: 'apple-touch-icon', | ||
| sizes: '180x180', | ||
| href: '/apple-touch-icon.png', | ||
| }, | ||
| { | ||
| rel: 'icon', | ||
| type: 'image/png', | ||
| sizes: '32x32', | ||
| href: '/favicon-32x32.png', | ||
| }, | ||
| { | ||
| rel: 'icon', | ||
| type: 'image/png', | ||
| sizes: '16x16', | ||
| href: '/favicon-16x16.png', | ||
| }, | ||
| { rel: 'manifest', href: '/site.webmanifest', color: '#fffff' }, | ||
| { rel: 'icon', href: '/favicon.ico' }, | ||
| ], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clean up manifest link metadata.
color: '#fffff' is invalid (missing one "f") and color doesn’t apply to rel="manifest". Use a meta theme-color instead or add a mask-icon link if needed.
- { rel: 'manifest', href: '/site.webmanifest', color: '#fffff' },
+ { rel: 'manifest', href: '/site.webmanifest' },
+ // Optional: Add theme color
+ // { name: 'theme-color', content: '#ffffff' },📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| links: [ | |
| { rel: 'stylesheet', href: appCss }, | |
| { | |
| rel: 'apple-touch-icon', | |
| sizes: '180x180', | |
| href: '/apple-touch-icon.png', | |
| }, | |
| { | |
| rel: 'icon', | |
| type: 'image/png', | |
| sizes: '32x32', | |
| href: '/favicon-32x32.png', | |
| }, | |
| { | |
| rel: 'icon', | |
| type: 'image/png', | |
| sizes: '16x16', | |
| href: '/favicon-16x16.png', | |
| }, | |
| { rel: 'manifest', href: '/site.webmanifest', color: '#fffff' }, | |
| { rel: 'icon', href: '/favicon.ico' }, | |
| ], | |
| links: [ | |
| { rel: 'stylesheet', href: appCss }, | |
| { | |
| rel: 'apple-touch-icon', | |
| sizes: '180x180', | |
| href: '/apple-touch-icon.png', | |
| }, | |
| { | |
| rel: 'icon', | |
| type: 'image/png', | |
| sizes: '32x32', | |
| href: '/favicon-32x32.png', | |
| }, | |
| { | |
| rel: 'icon', | |
| type: 'image/png', | |
| sizes: '16x16', | |
| href: '/favicon-16x16.png', | |
| }, | |
| { rel: 'manifest', href: '/site.webmanifest' }, | |
| // Optional: Add theme color | |
| // { name: 'theme-color', content: '#ffffff' }, | |
| { rel: 'icon', href: '/favicon.ico' }, | |
| ], |
🤖 Prompt for AI Agents
In examples/solid/start-basic-supabase/src/routes/__root.tsx around lines 55 to
76, the manifest link includes an invalid and misplaced property color:
'#fffff'; remove the color attribute from the manifest link, and instead add a
separate meta tag for theme-color (e.g., theme-color: '#ffffff') in the document
head; if a Safari pinned tab icon is desired add a mask-icon link with a proper
color attribute (and an SVG icon) rather than attaching color to the manifest
link.
| <Link | ||
| to="/posts" | ||
| activeProps={{ | ||
| class: 'font-bold', | ||
| }} | ||
| > | ||
| Posts | ||
| </Link> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix nav: Posts should link to the authed posts route.
/_authed/posts is the defined route; /posts 404s.
As per coding guidelines
- <Link
- to="/posts"
+ <Link
+ to="/_authed/posts"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <Link | |
| to="/posts" | |
| activeProps={{ | |
| class: 'font-bold', | |
| }} | |
| > | |
| Posts | |
| </Link> | |
| <Link | |
| to="/_authed/posts" | |
| activeProps={{ | |
| class: 'font-bold', | |
| }} | |
| > | |
| Posts | |
| </Link> |
🤖 Prompt for AI Agents
In examples/solid/start-basic-supabase/src/routes/__root.tsx around lines 117 to
124, the navigation Link currently points to "/posts" which 404s; update the
Link's "to" prop to the authenticated route "/_authed/posts" (keeping the
existing activeProps/class and other attributes unchanged) so the Posts nav
navigates to the defined authed posts route.
| params={{ | ||
| postId: post.id, | ||
| }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure postId param is a string.
post.id can be numeric from the API; params expect strings. Coerce to string at the call site.
As per coding guidelines
- params={{
- postId: post.id,
- }}
+ params={{
+ postId: String(post.id),
+ }}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| params={{ | |
| postId: post.id, | |
| }} | |
| params={{ | |
| postId: String(post.id), | |
| }} |
🤖 Prompt for AI Agents
In examples/solid/start-basic-supabase/src/routes/_authed/posts.tsx around lines
21 to 23, the postId parameter is being passed as post.id which may be numeric
from the API, but the params object expects string values. Coerce post.id to a
string by converting it at the call site when assigning it to the postId
parameter to ensure type consistency with the params expectations.
| function SignupComp() { | ||
| const signupMutation = useMutation({ | ||
| fn: useServerFn(signupFn), | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Fix payload shape to useServerFn and pass redirect param.
The server fn expects the validated input directly, not nested under data. Also plumb the redirect search param through.
function SignupComp() {
- const signupMutation = useMutation({
+ const signupMutation = useMutation({
fn: useServerFn(signupFn),
})
+ const search = Route.useSearch?.() ?? {}
...
- signupMutation.mutate({
- data: {
- email: formData.get('email') as string,
- password: formData.get('password') as string,
- },
- })
+ signupMutation.mutate({
+ email: (formData.get('email') as string) ?? '',
+ password: (formData.get('password') as string) ?? '',
+ redirectUrl:
+ typeof (search as any).redirect === 'string'
+ ? (search as any).redirect
+ : undefined,
+ })If you prefer stricter types, declare the mutation generics:
const signupMutation = useMutation<
{ email: string; password: string; redirectUrl?: string },
{ error: true; message: string }
>({ fn: useServerFn(signupFn) })Run to find other { data: { ... } } server-fn calls:
Also applies to: 46-51
🏁 Script executed:
#!/bin/bash
rg -nP --type=ts --type=tsx -C2 '\buseServerFn\([^)]+\)\)(\s*\))?\s*\)\s*\.mutate\(\s*\{\s*data\s*:' || rg -nP --type=ts --type=tsx -C2 '\.mutate\(\s*\{\s*data\s*:'Length of output: 55
I'll generate a corrected shell script to search for these patterns using glob patterns instead of type specifications, and also inspect the actual file to verify the issue.
🏁 Script executed:
#!/bin/bash
# Search for { data: { ... } } patterns in mutate calls using glob pattern
echo "=== Searching for .mutate({ data: ... }) patterns in TS/TSX files ==="
rg -nP -g '*.{ts,tsx}' '\.mutate\(\s*\{\s*data\s*:' -C2
echo ""
echo "=== Inspecting the signup.tsx file ==="
fd -e tsx 'signup.tsx' --exec cat {}Length of output: 13567
Fix payload shape and add redirect parameter to signup mutation.
The server function expects the validated input directly ({ email, password, redirectUrl? }), but the mutation passes it nested under data. Also extract and pass the redirect search param to enable post-signup navigation.
function SignupComp() {
const signupMutation = useMutation({
fn: useServerFn(signupFn),
})
+ const search = Route.useSearch?.() ?? {}
return (
<Auth
actionText="Sign Up"
status={signupMutation.status()}
onSubmit={(e) => {
const formData = new FormData(e.target as HTMLFormElement)
signupMutation.mutate({
- data: {
- email: formData.get('email') as string,
- password: formData.get('password') as string,
- },
+ email: (formData.get('email') as string) ?? '',
+ password: (formData.get('password') as string) ?? '',
+ redirectUrl:
+ typeof (search as any).redirect === 'string'
+ ? (search as any).redirect
+ : undefined,
})
}}For stricter types, declare mutation generics:
const signupMutation = useMutation<
{ email: string; password: string; redirectUrl?: string },
{ error: true; message: string }
>({ fn: useServerFn(signupFn) })🤖 Prompt for AI Agents
In examples/solid/start-basic-supabase/src/routes/signup.tsx around lines 34 to
37, the signup mutation currently nests the payload under a data property and
doesn’t pass the redirect search param or proper generics; change the
useMutation to declare generics for the input and error types, extract the
redirect (e.g. from location.search or URLSearchParams) and pass it as
redirectUrl, and call the mutation with the flat object { email, password,
redirectUrl } (not { data: ... }) so the server function receives the expected
shape.
| export type PostType = { | ||
| id: string | ||
| title: string | ||
| body: string | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Align PostType.id with API (number) and coerce route params to string at call sites.
JSONPlaceholder returns numeric id. Typing it as string masks mismatches under strict mode and leaks into route params. Use number here and String(post.id) where building params (see posts.tsx).
As per coding guidelines
export type PostType = {
- id: string
+ id: number
title: string
body: string
}
@@
- .get<PostType>(`https://jsonplaceholder.typicode.com/posts/${postId}`)
+ .get<PostType>(`https://jsonplaceholder.typicode.com/posts/${postId}`)
@@
- .get<Array<PostType>>('https://jsonplaceholder.typicode.com/posts')
+ .get<Array<PostType>>('https://jsonplaceholder.typicode.com/posts')Follow-up in examples/solid/start-basic-supabase/src/routes/_authed/posts.tsx:
params: { postId: String(post.id) }(see related comment there).
Also applies to: 15-17, 33-35
🤖 Prompt for AI Agents
In examples/solid/start-basic-supabase/src/utils/posts.ts around lines 5 to 9
(and also update occurrences at lines 15-17 and 33-35), PostType.id is currently
typed as string but the API (JSONPlaceholder) returns numeric ids; change
PostType.id to number, then update any call sites that pass ids into route
params to coerce to string (e.g., use String(post.id) when building params in
posts.tsx and routes/_authed/posts.tsx). Ensure all imports/usages reflect the
numeric id type and convert to string only at the boundary where a URL/route
param is required.
|
@birkskyum you need a supabase key, add to .env and will work. ty for adding to the config.json, I couldn't get tanstack.com running locally |


Migrate the react supabase solid start example to solid. Works identical to the react version.
Summary by CodeRabbit
New Features
Documentation