Skip to content

Conversation

@juliansommer
Copy link

@juliansommer juliansommer commented Oct 24, 2025

Migrate the react supabase solid start example to solid. Works identical to the react version.

Summary by CodeRabbit

  • New Features

    • Added a new Solid.js + Supabase example starter project with built-in authentication (login/signup), protected routes, post management interface, and dark mode support.
  • Documentation

    • Updated documentation to showcase the new starter example.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 24, 2025

Walkthrough

Adds 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

Cohort / File(s) Change Summary
Documentation Configuration
docs/start/config.json
Added new example entry "Basic + Supabase" under Solid framework examples referencing framework/solid/examples/start-basic-supabase.
Project Configuration
examples/solid/start-basic-supabase/package.json, tsconfig.json, vite.config.ts, .gitignore, .prettierignore
Establishes project metadata, build tooling configuration (Vite with Solid and TanStack plugins), TypeScript strict settings, port 3000 dev server, and standard ignore patterns for dependencies, builds, and artifacts.
Authentication & UI Components
src/components/Auth.tsx, src/components/Login.tsx, src/components/NotFound.tsx, src/components/DefaultCatchBoundary.tsx
Implements reusable Auth form component, Login form with signup fallback, NotFound error boundary, and error boundary wrapper with navigation recovery.
Hooks & Mutations
src/hooks/useMutation.ts
Exports useMutation hook managing async function state (status, variables, error, data) with success/error callbacks.
Router Setup
src/router.tsx, src/routeTree.gen.ts
Defines router factory function with module augmentation for type-safe Register interface and auto-generated route tree with strongly-typed path/route mappings.
Root & Authentication Routes
src/routes/__root.tsx, src/routes/_authed.tsx, src/routes/login.tsx, src/routes/logout.tsx, src/routes/signup.tsx
Implements root layout with Supabase user fetching, authenticated route guard, and login/logout/signup flows with server-side validation.
Posts Routes
src/routes/index.tsx, src/routes/_authed/posts.tsx, src/routes/_authed/posts.index.tsx, src/routes/_authed/posts.$postId.tsx
Defines nested route hierarchy for authenticated posts listing with dynamic post detail view and 404 handling.
Utilities & Styling
src/utils/supabase.ts, src/utils/posts.ts, src/utils/seo.ts, src/styles/app.css
Provides Supabase server client with SSR cookie bridge, POST/API fetching utilities, SEO metadata helper, and Tailwind-based dark-mode aware styling.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • File count: ~25 files across multiple categories (components, routes, utilities, config)
  • Integration complexity: Supabase server-client setup with SSR cookie bridging requires verification of security and cookie handling
  • Route structure: Multiple nested routes with guards, loaders, and error boundaries need validation for correctness
  • Server functions: Multiple createServerFn implementations (loginFn, signupFn, fetchPost, fetchPosts, logoutFn) with input validation and error handling require individual scrutiny
  • State management: useMutation hook and route context patterns need review for correctness and consistency

Areas requiring extra attention:

  • Supabase server client cookie adapter (SSR security implications)
  • Authentication flow: loginFn and signupFn error handling and redirect logic
  • Route guards: _authed route beforeLoad validation and error boundary triggering
  • Posts API fetching: artificial delay in fetchPosts and 404 handling via notFound()

Possibly related PRs

Suggested labels

documentation, example, solid

Suggested reviewers

  • brenelz

Poem

🐰 A Solid Start with Supabase dreams,
Auth flows dancing in moonlit streams,
Posts cascade through nested routes so true,
SSR cookies bind server and you,
Hopping onwards to /posts anew! 🌙

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "Create Supabase Solid Start Example" accurately reflects the main changeset, which adds a comprehensive new example directory under examples/solid/start-basic-supabase/ with all necessary components, routes, utilities, and configuration files for a Supabase-integrated Solid Start application. The title is concise (5 words), specific, and clearly conveys the primary change without vague terminology or unnecessary details. A teammate scanning the repository history would immediately understand that this PR introduces a new Supabase Solid Start example, making it an effective summary of the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 explicit

ctx.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 mutate

Avoid 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 errors

Matching '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 fn

signupFn 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 helper

Nice-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 directly

The 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 content

Current 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 parity

Matches 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

📥 Commits

Reviewing files that changed from the base of the PR and between d6f5870 and 6ea8840.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is 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.tsx
  • examples/solid/start-supabase-basic/src/components/DefaultCatchBoundary.tsx
  • examples/solid/start-supabase-basic/src/routes/_authed/posts.index.tsx
  • examples/solid/start-supabase-basic/src/components/NotFound.tsx
  • examples/solid/start-supabase-basic/src/utils/supabase.ts
  • examples/solid/start-supabase-basic/src/routes/logout.tsx
  • examples/solid/start-supabase-basic/src/components/Login.tsx
  • examples/solid/start-supabase-basic/src/utils/posts.ts
  • examples/solid/start-supabase-basic/src/hooks/useMutation.ts
  • examples/solid/start-supabase-basic/src/router.tsx
  • examples/solid/start-supabase-basic/src/routes/signup.tsx
  • examples/solid/start-supabase-basic/src/routes/login.tsx
  • examples/solid/start-supabase-basic/src/components/Auth.tsx
  • examples/solid/start-supabase-basic/src/utils/seo.ts
  • examples/solid/start-supabase-basic/src/routes/_authed.tsx
  • examples/solid/start-supabase-basic/src/routes/__root.tsx
  • examples/solid/start-supabase-basic/src/routes/_authed/posts.$postId.tsx
  • examples/solid/start-supabase-basic/vite.config.ts
  • examples/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.tsx
  • examples/solid/start-supabase-basic/src/routes/_authed/posts.index.tsx
  • examples/solid/start-supabase-basic/src/routes/logout.tsx
  • examples/solid/start-supabase-basic/src/routes/signup.tsx
  • examples/solid/start-supabase-basic/src/routes/login.tsx
  • examples/solid/start-supabase-basic/src/routes/_authed.tsx
  • examples/solid/start-supabase-basic/src/routes/__root.tsx
  • examples/solid/start-supabase-basic/src/routes/_authed/posts.$postId.tsx
  • examples/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.tsx
  • examples/solid/start-supabase-basic/src/components/DefaultCatchBoundary.tsx
  • examples/solid/start-supabase-basic/src/routes/_authed/posts.index.tsx
  • examples/solid/start-supabase-basic/src/components/NotFound.tsx
  • examples/solid/start-supabase-basic/package.json
  • examples/solid/start-supabase-basic/src/styles/app.css
  • examples/solid/start-supabase-basic/tsconfig.json
  • examples/solid/start-supabase-basic/src/utils/supabase.ts
  • examples/solid/start-supabase-basic/src/routes/logout.tsx
  • examples/solid/start-supabase-basic/src/components/Login.tsx
  • examples/solid/start-supabase-basic/src/utils/posts.ts
  • examples/solid/start-supabase-basic/src/hooks/useMutation.ts
  • examples/solid/start-supabase-basic/src/router.tsx
  • examples/solid/start-supabase-basic/src/routes/signup.tsx
  • examples/solid/start-supabase-basic/src/routes/login.tsx
  • examples/solid/start-supabase-basic/src/components/Auth.tsx
  • examples/solid/start-supabase-basic/src/utils/seo.ts
  • examples/solid/start-supabase-basic/src/routes/_authed.tsx
  • examples/solid/start-supabase-basic/src/routes/__root.tsx
  • examples/solid/start-supabase-basic/src/routes/_authed/posts.$postId.tsx
  • examples/solid/start-supabase-basic/vite.config.ts
  • examples/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/ and public/ are standard directories, pnpm-lock.yaml is a tool-generated lock file, and routeTree.gen.ts is a generated router configuration that should not be formatted.

examples/solid/start-supabase-basic/src/routes/_authed/posts.index.tsx (1)

3-9: LGTM

Index child route and component are straightforward and correct.

examples/solid/start-supabase-basic/src/routes/index.tsx (1)

3-13: LGTM

Root route wiring looks good.

@@ -0,0 +1,20 @@
node_modules
# Keep environment variables out of version control
!.env
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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.json

If 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.example

Also 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".

Comment on lines +15 to +16
/public/build# Sentry Config File
.env.sentry-build-plugin
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
/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.

Comment on lines +8 to +11
"dev": "vite dev",
"build": "vite build && tsc --noEmit",
"start": "vite start"
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
"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.

Comment on lines +16 to +21
"@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",
Copy link
Contributor

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.

Suggested change
"@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.

Comment on lines +11 to +20
const loginMutation = useMutation({
fn: loginFn,
onSuccess: async (ctx) => {
if (!ctx.data?.error) {
await router.invalidate()
router.navigate({ to: '/' })
return
}
},
})
Copy link
Contributor

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.

Suggested change
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.

Comment on lines 103 to 139
<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 />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
<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.

Comment on lines +20 to +27
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>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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.

Suggested change
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.

Comment on lines +9 to +19
if (error) {
return {
error: true,
message: error.message,
}
}

throw redirect({
href: '/',
})
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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.

Comment on lines +11 to +24
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
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment on lines +17 to +19
cookies.forEach((cookie) => {
setCookie(cookie.name, cookie.value)
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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.

Suggested change
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.

@brenelz brenelz requested a review from birkskyum October 24, 2025 03:24
@birkskyum
Copy link
Member

birkskyum commented Oct 24, 2025

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:

Copy link
Member

@birkskyum birkskyum left a 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

@github-actions github-actions bot added the documentation Everything documentation related label Oct 26, 2025
@nx-cloud
Copy link

nx-cloud bot commented Oct 26, 2025

View your CI Pipeline Execution ↗ for commit 03fe120

Command Status Duration Result
nx run-many --targets=test:eslint,test:unit,tes... ⏳ In Progress ... View ↗

☁️ Nx Cloud last updated this comment at 2025-10-26 14:07:02 UTC

@birkskyum
Copy link
Member

I see this when i start the example
Screenshot 2025-10-26 at 14 59 42

@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 26, 2025

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@5598

@tanstack/directive-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/directive-functions-plugin@5598

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/eslint-plugin-router@5598

@tanstack/history

npm i https://pkg.pr.new/TanStack/router/@tanstack/history@5598

@tanstack/nitro-v2-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/nitro-v2-vite-plugin@5598

@tanstack/react-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router@5598

@tanstack/react-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@5598

@tanstack/react-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-ssr-query@5598

@tanstack/react-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@5598

@tanstack/react-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@5598

@tanstack/react-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@5598

@tanstack/router-cli

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-cli@5598

@tanstack/router-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-core@5598

@tanstack/router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools@5598

@tanstack/router-devtools-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools-core@5598

@tanstack/router-generator

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-generator@5598

@tanstack/router-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@5598

@tanstack/router-ssr-query-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-ssr-query-core@5598

@tanstack/router-utils

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-utils@5598

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-vite-plugin@5598

@tanstack/server-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/server-functions-plugin@5598

@tanstack/solid-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router@5598

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-devtools@5598

@tanstack/solid-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-ssr-query@5598

@tanstack/solid-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@5598

@tanstack/solid-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@5598

@tanstack/solid-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@5598

@tanstack/start-client-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@5598

@tanstack/start-plugin-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@5598

@tanstack/start-server-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@5598

@tanstack/start-static-server-functions

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-static-server-functions@5598

@tanstack/start-storage-context

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-storage-context@5598

@tanstack/valibot-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@5598

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@5598

@tanstack/zod-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@5598

commit: 03fe120

@birkskyum birkskyum changed the title Create Supabase Solid Start Example docs(solid-start): add Supabase example Oct 26, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 .env negation pattern—environment files should not be committed.

The negation pattern !.env on 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 File is 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 to setCookie.

examples/solid/start-basic-supabase/src/routes/logout.tsx (1)

9-14: Handle logout errors with a throw or redirect; don't return data

Returning 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 boundaries

The login mutation calls loginFn directly while the signup mutation correctly wraps signupFn with useServerFn (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 via err.response.status.

err.status is undefined; 404s won’t map to notFound().

-      .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/error linger; on catch, data isn’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 export

The PostErrorComponent simply forwards to ErrorComponent, which is already directly assigned as the route's errorComponent (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: Use property for Open Graph tags and skip meta with undefined content.

Current code may emit <meta content="undefined"> and OG scrapers expect property="og:*". Build tags conditionally and switch OG to property.

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 moving loginFn to a server utils module.

_authed.tsx imports Login, and Login appears to use loginFn. This can create an ESM cycle and undefined imports at runtime. Extract loginFn (and signupFn) to src/server/auth.ts (or utils/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 onSubmit only sends email and password — the redirectUrl parameter 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 resolveRedirect already 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

📥 Commits

Reviewing files that changed from the base of the PR and between 6ea8840 and 03fe120.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is 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.tsx
  • examples/solid/start-basic-supabase/src/routes/logout.tsx
  • examples/solid/start-basic-supabase/src/components/DefaultCatchBoundary.tsx
  • examples/solid/start-basic-supabase/src/utils/supabase.ts
  • examples/solid/start-basic-supabase/src/components/NotFound.tsx
  • examples/solid/start-basic-supabase/vite.config.ts
  • examples/solid/start-basic-supabase/src/routes/index.tsx
  • examples/solid/start-basic-supabase/src/routes/_authed/posts.tsx
  • examples/solid/start-basic-supabase/src/components/Auth.tsx
  • examples/solid/start-basic-supabase/src/router.tsx
  • examples/solid/start-basic-supabase/src/routes/_authed/posts.$postId.tsx
  • examples/solid/start-basic-supabase/src/routes/_authed.tsx
  • examples/solid/start-basic-supabase/src/hooks/useMutation.ts
  • examples/solid/start-basic-supabase/src/routeTree.gen.ts
  • examples/solid/start-basic-supabase/src/routes/login.tsx
  • examples/solid/start-basic-supabase/src/routes/signup.tsx
  • examples/solid/start-basic-supabase/src/routes/__root.tsx
  • examples/solid/start-basic-supabase/src/utils/posts.ts
  • examples/solid/start-basic-supabase/src/components/Login.tsx
  • examples/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.tsx
  • examples/solid/start-basic-supabase/src/routes/logout.tsx
  • examples/solid/start-basic-supabase/src/routes/index.tsx
  • examples/solid/start-basic-supabase/src/routes/_authed/posts.tsx
  • examples/solid/start-basic-supabase/src/routes/_authed/posts.$postId.tsx
  • examples/solid/start-basic-supabase/src/routes/_authed.tsx
  • examples/solid/start-basic-supabase/src/routes/login.tsx
  • examples/solid/start-basic-supabase/src/routes/signup.tsx
  • examples/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.tsx
  • examples/solid/start-basic-supabase/src/routes/logout.tsx
  • examples/solid/start-basic-supabase/src/components/DefaultCatchBoundary.tsx
  • examples/solid/start-basic-supabase/tsconfig.json
  • examples/solid/start-basic-supabase/src/utils/supabase.ts
  • examples/solid/start-basic-supabase/src/components/NotFound.tsx
  • examples/solid/start-basic-supabase/vite.config.ts
  • examples/solid/start-basic-supabase/src/routes/index.tsx
  • examples/solid/start-basic-supabase/src/routes/_authed/posts.tsx
  • examples/solid/start-basic-supabase/src/components/Auth.tsx
  • examples/solid/start-basic-supabase/src/router.tsx
  • examples/solid/start-basic-supabase/src/routes/_authed/posts.$postId.tsx
  • examples/solid/start-basic-supabase/src/routes/_authed.tsx
  • examples/solid/start-basic-supabase/src/hooks/useMutation.ts
  • examples/solid/start-basic-supabase/src/routeTree.gen.ts
  • examples/solid/start-basic-supabase/src/routes/login.tsx
  • examples/solid/start-basic-supabase/src/routes/signup.tsx
  • examples/solid/start-basic-supabase/src/routes/__root.tsx
  • examples/solid/start-basic-supabase/src/utils/posts.ts
  • examples/solid/start-basic-supabase/src/components/Login.tsx
  • examples/solid/start-basic-supabase/package.json
  • examples/solid/start-basic-supabase/src/utils/seo.ts
  • examples/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 good

The 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 /login and /logout exist—no action required.

Both routes are properly defined in the codebase (src/routes/login.tsx and src/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.tsx exports loginFn (server function at line 6)
  • Login.tsx imports loginFn from _authed and uses it
  • _authed.tsx renders <Login /> component in errorComponent
  • Standalone /login route exists at src/routes/login.tsx

This 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 /login or consolidate login logic).

examples/solid/start-basic-supabase/src/routeTree.gen.ts (1)

1-225: Autogenerated file — skipping review.

routeTree.gen.ts is 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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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.

Suggested change
"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.

Comment on lines +18 to +20
"@tanstack/solid-router": "^1.133.25",
"@tanstack/solid-router-devtools": "^1.133.25",
"@tanstack/solid-start": "^1.133.26",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
"@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.

Comment on lines +21 to +29
<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"
/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
<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">
Email
</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).

Comment on lines +55 to +76
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' },
],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
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.

Comment on lines +117 to +124
<Link
to="/posts"
activeProps={{
class: 'font-bold',
}}
>
Posts
</Link>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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.

Suggested change
<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.

Comment on lines +21 to +23
params={{
postId: post.id,
}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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.

Comment on lines +34 to +37
function SignupComp() {
const signupMutation = useMutation({
fn: useServerFn(signupFn),
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 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.

Comment on lines +5 to +9
export type PostType = {
id: string
title: string
body: string
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

@juliansommer
Copy link
Author

juliansommer commented Oct 26, 2025

I see this when i start the example Screenshot 2025-10-26 at 14 59 42

@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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Everything documentation related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants