Skip to content

Example: Add example with Appwrite Auth#7225

Open
ChiragAgg5k wants to merge 2 commits intoTanStack:mainfrom
ChiragAgg5k:example/start-appwrite-basic
Open

Example: Add example with Appwrite Auth#7225
ChiragAgg5k wants to merge 2 commits intoTanStack:mainfrom
ChiragAgg5k:example/start-appwrite-basic

Conversation

@ChiragAgg5k
Copy link
Copy Markdown

@ChiragAgg5k ChiragAgg5k commented Apr 19, 2026

What does it do

Adds a new React Start example, examples/react/start-appwrite-basic, that demonstrates end-to-end authentication with Appwrite — the same feature surface as start-supabase-basic (sign up, log in, log out, a root fetchUser, and a _authed layout that gates /posts).

Shape mirrors start-supabase-basic so the two examples are directly comparable; only the auth utility and the server functions that call it differ.

What's inside

  • src/utils/appwrite.ts — Appwrite SSR helpers built on node-appwrite:
    • createSessionClient() reads the session secret from the appwrite-session cookie (via @tanstack/react-start/server's getCookie) and returns an Account client scoped to that session.
    • createAdminClient() uses the server APPWRITE_API_KEY for the sign-up / sign-in handshakes that need to issue new sessions.
    • setAppwriteSessionCookie / clearAppwriteSessionCookie cookie helpers.
  • src/routes/_authed.tsxloginFn server function → account.createEmailPasswordSession + cookie; beforeLoad gate + Login error component.
  • src/routes/signup.tsxsignupFnaccount.create + session + cookie + redirect.
  • src/routes/logout.tsx — session delete + cookie clear + redirect.
  • src/routes/__root.tsxfetchUser via the session client's account.get().
  • docs/start/config.json — adds "Basic + Appwrite Auth" under React examples.
  • docs/start/framework/react/guide/authentication-overview.md — lists the new example alongside the Supabase one under DIY Implementations.

The mock posts data layer (jsonplaceholder via redaxios) is unchanged, matching the Supabase example.

How to run

pnpm install
cd examples/react/start-appwrite-basic
# fill in real values in .env
pnpm dev
APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
APPWRITE_PROJECT_ID=your-project-id
APPWRITE_API_KEY=your-server-api-key

An Appwrite project with Email/Password auth enabled and a server API Key that has sessions.write and users.write scopes.

Testing

Verified against a real Appwrite Cloud project end-to-end:

  1. Direct SDK probe (via node-appwrite): admin account.createcreateEmailPasswordSessionaccount.get with session client → session delete → admin user delete. All green.
  2. SSR cookie pipeline (cURL + SDK-planted cookie):
    • GET / without cookie → SSR payload contains user:null.
    • GET / with a valid appwrite-session=<secret> cookie → SSR payload renders the user's email.
    • GET /posts with cookie → 200, nav shows "Logout".
    • GET /posts without cookie → _authed.errorComponent renders the Login form.
  3. Browser flow (Playwright) covered sign up → redirect → nav shows email → protected /posts renders → logout → /posts falls back to Login → bad-creds shows the inline error and the "Sign up instead?" fallback → login with correct creds lands back on Home.

All screenshots below are captured against the running dev server.

Signed-out Home

home unauth

Sign-up form (empty → filled)

signup empty

signup filled

After sign up — auto-logged in, nav shows user email + Logout

home authed

/posts with session (the _authed layout passes through)

posts authed

/posts after logout — _authed.errorComponent renders the Login form

posts unauthed fallback

Invalid credentials — Appwrite error surfaced inline, "Sign up instead?" fallback

login invalid

Valid login redirects back to Home

login success

Local checks

  • pnpm nx run-many --target=build --projects=@tanstack/react-start,@tanstack/react-router,… — clean.
  • tsc --noEmit in the example — clean.
  • vite build && tsc --noEmit (the example's build script) — clean.
  • pnpm exec eslint examples/react/start-appwrite-basic — 5 errors, all pre-existing in the Supabase template (sort-imports on createFileRoute, newline-after-import in two route files, and one no-unnecessary-condition — identical baseline).

Happy to adjust the folder name, README wording, or add a Solid mirror (examples/solid/start-appwrite-basic) if that would be preferred in a follow-up.

Summary by CodeRabbit

  • New Features

    • Added a new React + TanStack Start example showcasing Appwrite email/password auth with sign-up, sign-in, logout, SSR-safe session handling, and a simple posts browsing demo.
  • Documentation

    • Added navigation links and an example guide with setup and usage instructions for the Appwrite authentication example.
  • Chores

    • Included local dev container and project configuration for easier onboarding.

Mirrors the start-supabase-basic example but uses Appwrite for
email/password authentication. Adds a React Start example that
demonstrates the canonical Appwrite server-side rendering flow:

- session secret stored in an httpOnly `appwrite-session` cookie
- `createSessionClient` / `createAdminClient` helpers over `node-appwrite`
- server functions for login, signup, logout and a root `fetchUser`
- `_authed` layout route that gates `/posts` and renders the Login
  form as its error component when the user is not authenticated
- docs/start/config.json + docs/start/framework/react/guide/authentication-overview.md
  updated to surface the new example alongside start-supabase-basic
@github-actions github-actions Bot added the documentation Everything documentation related label Apr 19, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 93df4b8a-272d-4831-b4c8-d39cbb362f84

📥 Commits

Reviewing files that changed from the base of the PR and between 080edcb and 5cdb14e.

📒 Files selected for processing (3)
  • examples/react/start-appwrite-basic/src/routes/__root.tsx
  • examples/react/start-appwrite-basic/src/routes/signup.tsx
  • examples/react/start-appwrite-basic/src/utils/appwrite.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/react/start-appwrite-basic/src/routes/signup.tsx
  • examples/react/start-appwrite-basic/src/utils/appwrite.ts

📝 Walkthrough

Walkthrough

Adds a complete React example demonstrating TanStack Start + Appwrite authentication: project scaffolding, routing, server functions for auth, protected routes, UI components, Appwrite utilities, data-fetching examples, configuration, and documentation.

Changes

Cohort / File(s) Summary
Docs
docs/start/config.json, docs/start/framework/react/guide/authentication-overview.md
Added navigation entries and an "Appwrite Auth" example link.
Example root / tooling
examples/react/start-appwrite-basic/.devcontainer/devcontainer.json, examples/react/start-appwrite-basic/.env, examples/react/start-appwrite-basic/.gitignore, examples/react/start-appwrite-basic/.prettierignore, examples/react/start-appwrite-basic/.vscode/settings.json, examples/react/start-appwrite-basic/package.json, examples/react/start-appwrite-basic/tsconfig.json, examples/react/start-appwrite-basic/vite.config.ts
Project configuration, devcontainer, env placeholders, git/prettier/vscode ignores, npm scripts, dependencies, TypeScript and Vite config.
Docs / README
examples/react/start-appwrite-basic/README.md
New README documenting setup, env vars, prerequisites, and example features.
Router infra
examples/react/start-appwrite-basic/src/routeTree.gen.ts, examples/react/start-appwrite-basic/src/router.tsx, examples/react/start-appwrite-basic/src/routes/__root.tsx
Generated route tree, router creation, root route with SSR user fetch, document head, error/not-found wiring.
Auth routes & server fns
examples/react/start-appwrite-basic/src/routes/_authed.tsx, examples/react/start-appwrite-basic/src/routes/login.tsx, examples/react/start-appwrite-basic/src/routes/signup.tsx, examples/react/start-appwrite-basic/src/routes/logout.tsx
Server functions for login/signup/logout, session cookie management, auth guard beforeLoad, and route components.
Posts routes
examples/react/start-appwrite-basic/src/routes/_authed/posts.tsx, .../posts.index.tsx, .../posts.$postId.tsx
Authenticated posts list and detail routes using server fetchers (JSONPlaceholder) and nested routing.
Components
examples/react/start-appwrite-basic/src/components/Auth.tsx, .../Login.tsx, .../DefaultCatchBoundary.tsx, .../NotFound.tsx
UI components: auth form, login page with mutations and signup fallback, error boundary, and 404 page.
Hooks & utilities
examples/react/start-appwrite-basic/src/hooks/useMutation.ts, .../utils/appwrite.ts, .../utils/posts.ts, .../utils/seo.ts
Custom mutation hook, Appwrite client/session helpers, post fetcher server functions, and SEO metadata builder.
Styles
examples/react/start-appwrite-basic/src/styles/app.css
Tailwind-based global styles and dark-mode support.

Sequence Diagrams

sequenceDiagram
    rect rgba(220, 235, 255, 0.5)
    actor User
    end
    participant Client as React Client
    participant Server as TanStack Start (Server Fns)
    participant Appwrite as Appwrite Backend

    User->>Client: Submit login/signup form
    Client->>Server: POST email & password (server fn)
    Server->>Appwrite: createEmailPasswordSession(email,password)
    alt Success
        Appwrite-->>Server: Session created
        Server->>Server: set session cookie (session-secret)
        Server-->>Client: { error: false }
        Client->>Client: Invalidate cache & navigate home
    else Failure
        Appwrite-->>Server: Error (invalid credentials)
        Server-->>Client: { error: true, message }
        Client->>Client: Show error, offer signup
    end
Loading
sequenceDiagram
    participant Client as React Client
    participant Server as TanStack Start (beforeLoad)
    participant Appwrite as Appwrite Backend

    Client->>Server: Navigate to protected route (/_authed)
    Server->>Appwrite: account.get() using session cookie
    alt Valid session
        Appwrite-->>Server: { email: "..." }
        Server->>Server: Attach user to route context
        Server-->>Client: Render protected content
    else No session / invalid
        Appwrite-->>Server: Error / null
        Server-->>Client: Throw "Not authenticated"
        Client->>Client: Render Login component
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐇 I hopped in with cookies and routes to share,
Set sessions and guards with careful care,
Forms that submit and posts that show,
Appwrite and Start in a tidy flow,
A tiny rabbit's cheer for this new pair!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Example: Add example with Appwrite Auth' is clear and directly summarizes the main change: adding a new example with Appwrite authentication.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Copy Markdown
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: 10

🧹 Nitpick comments (2)
examples/react/start-appwrite-basic/src/utils/appwrite.ts (1)

10-18: Replace env non-null assertions with runtime validation.

The ! assertions hide missing Appwrite config from TypeScript and defer failure to opaque SDK errors. Fail fast with explicit messages. As per coding guidelines, **/*.{ts,tsx}: Use TypeScript strict mode with extensive type safety.

Proposed refactor
+function requiredEnv(name: 'APPWRITE_PROJECT_ID' | 'APPWRITE_API_KEY') {
+  const value = process.env[name]
+  if (!value) {
+    throw new Error(`${name} is required`)
+  }
+  return value
+}
+
 function getEndpoint() {
-  return process.env.APPWRITE_ENDPOINT ?? 'https://cloud.appwrite.io/v1'
+  return process.env.APPWRITE_ENDPOINT || 'https://cloud.appwrite.io/v1'
 }
 
 export function createSessionClient() {
   const client = new Client()
     .setEndpoint(getEndpoint())
-    .setProject(process.env.APPWRITE_PROJECT_ID!)
+    .setProject(requiredEnv('APPWRITE_PROJECT_ID'))
@@
 export function createAdminClient() {
   const client = new Client()
     .setEndpoint(getEndpoint())
-    .setProject(process.env.APPWRITE_PROJECT_ID!)
-    .setKey(process.env.APPWRITE_API_KEY!)
+    .setProject(requiredEnv('APPWRITE_PROJECT_ID'))
+    .setKey(requiredEnv('APPWRITE_API_KEY'))

Also applies to: 31-35

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/react/start-appwrite-basic/src/utils/appwrite.ts` around lines 10 -
18, The code uses non-null assertions for Appwrite config
(process.env.APPWRITE_PROJECT_ID) in createSessionClient (and similarly where
APPWRITE_PROJECT_ID is used around lines 31-35); replace the `!` with runtime
validation: read process.env.APPWRITE_PROJECT_ID into a const, check if it is
undefined or empty, and throw a clear, descriptive Error (e.g.,
"APPWRITE_PROJECT_ID is required") before constructing the Client in
createSessionClient (and apply the same check where the variable is used
elsewhere) so TypeScript strictness is preserved and failures fail fast with
actionable messages.
examples/react/start-appwrite-basic/src/router.tsx (1)

1-17: Consider wiring DefaultCatchBoundary as the router's default error component.

DefaultCatchBoundary is defined in src/components/DefaultCatchBoundary.tsx but is not referenced from getRouter() here. Registering it as defaultErrorComponent (and optionally a defaultNotFoundComponent) on createRouter would ensure route-level errors are actually caught by it. Skip if this matches the sibling start-supabase-basic template intentionally.

Proposed change
-  const router = createRouter({
-    routeTree,
-    scrollRestoration: true,
-  })
+  const router = createRouter({
+    routeTree,
+    scrollRestoration: true,
+    defaultErrorComponent: DefaultCatchBoundary,
+  })
 import { createRouter } from '@tanstack/react-router'
 import { routeTree } from './routeTree.gen'
+import { DefaultCatchBoundary } from './components/DefaultCatchBoundary'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/react/start-appwrite-basic/src/router.tsx` around lines 1 - 17, The
router currently created in getRouter() doesn't set a default error component,
so route-level errors won't be handled by your DefaultCatchBoundary; update the
createRouter call inside getRouter to pass defaultErrorComponent:
DefaultCatchBoundary (and optionally defaultNotFoundComponent:
DefaultCatchBoundary) after importing DefaultCatchBoundary from
src/components/DefaultCatchBoundary.tsx so the router uses that component for
uncaught route errors and 404s.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@examples/react/start-appwrite-basic/.gitignore`:
- Around line 15-16: The ignore line currently contains the literal
"/public/build# Sentry Config File" so the /public/build path isn't actually
ignored; change the single line that contains "/public/build# Sentry Config
File" into two lines by separating the path and the comment: add a line with
"/public/build" and on the next line add the comment beginning with "#" (e.g.,
"# Sentry Config File"), leaving the existing ".env.sentry-build-plugin" entry
unchanged.

In `@examples/react/start-appwrite-basic/src/components/Auth.tsx`:
- Around line 24-42: Update the auth form in the Auth component so the fields
match email/password semantics: change the label text for the input with
id="email"/name="email" from "Username" to "Email", add required attributes to
both the email and password inputs (and consider adding autoComplete="email" and
autoComplete="current-password"), and add simple client-side validation (e.g.,
input type="email" already enforces format; ensure the password input has
required and a sensible minLength) on the inputs with id="email" and
id="password" so empty submissions are prevented.

In `@examples/react/start-appwrite-basic/src/components/Login.tsx`:
- Around line 11-24: The loginMutation currently calls loginFn directly which
bypasses Start's client wrapper—wrap loginFn with useServerFn (same pattern used
for signupMutation) so server responses, errors and redirects are handled
correctly; update the loginMutation's fn to useServerFn(loginFn) (keeping
useMutation and onSuccess logic intact) to match how signupMutation uses
useServerFn.

In `@examples/react/start-appwrite-basic/src/routes/__root.tsx`:
- Line 104: The root document's <html> tag is missing a lang attribute which is
an accessibility issue; update the <html> element in the root route (the line
that currently contains "<html>") to include an appropriate lang value (e.g.,
lang="en" or the app's primary locale) so screen readers and a11y tools can
detect the document language.
- Line 77: The manifest link entry uses an invalid 5-digit hex '#fffff' and
incorrectly includes a color attribute; remove the color property from the
manifest link object (the entry where rel: 'manifest') and, if a pinned-tab
color was intended, add a separate link with rel: 'mask-icon' and a valid
3/6/8-digit hex (e.g., '#fff' or '#ffffff') for the color attribute so browsers
accept it.

In `@examples/react/start-appwrite-basic/src/routes/signup.tsx`:
- Around line 15-33: The signup flow can leave an orphaned user if
account.create succeeds but account.createEmailPasswordSession fails; update the
handler so after calling account.create (which uses the generated userId) you
wrap the session creation in a try/catch: on session creation failure either (1)
roll back by calling account.delete(userId) to remove the partially-created user
before returning the error, or (2) attempt a deterministic fallback such as
retrying createEmailPasswordSession once and if it still fails return a specific
error flag (e.g., error.code = 'SESSION_CREATION_FAILED') so the caller can
attempt a login path; reference account.create,
account.createEmailPasswordSession, setAppwriteSessionCookie and the generated
userId to implement the rollback or fallback logic.
- Line 1: The named imports are out of alphabetical order on the import from
'@tanstack/react-router'; update the import specifier list so the named exports
are alphabetized (put createFileRoute before redirect) in the import statement
that currently mentions createFileRoute and redirect to satisfy ESLint import
order rules and save.

In `@examples/react/start-appwrite-basic/src/styles/app.css`:
- Around line 27-29: The rule ".using-mouse * { outline: none !important; }"
removes all focus outlines even when keyboard users are navigating; change it to
only remove outline for focus states that are not keyboard-visible (e.g., target
".using-mouse *:focus:not(:focus-visible)" or the equivalent
:focus:not(:focus-visible) selector) so that keyboard-visible focus indicators
remain visible while mouse-only focus still hides outlines.

In `@examples/react/start-appwrite-basic/src/utils/posts.ts`:
- Around line 5-17: The handler in fetchPost currently assumes the external
JSONPlaceholder payload matches PostType (id:string) but the API returns id as a
number; modify the fetch to parse/normalize the response before returning: call
axios.get with an appropriate external response shape (e.g., id:number,
title:string, body:string) or use any, then map the received object to PostType
by converting id to a string (e.g., id: String(response.id)) and copying
title/body; remove the incorrect generic <PostType> on axios.get and ensure the
handler returns the normalized PostType object.

In `@examples/react/start-appwrite-basic/src/utils/seo.ts`:
- Around line 12-30: The tags array in seo.ts currently uses name="og:*" and
always emits description/keywords which can be undefined; update the
construction of the tags array (the tags constant that references title,
description, keywords, image) to: 1) use property: 'og:*' for Open Graph entries
(e.g., property: 'og:title', property: 'og:description', property: 'og:type',
property: 'og:image') instead of name, and 2) conditionally push meta objects
only when their values exist (guard emitting description, keywords, twitter:*
and og:image when description/keywords/image are truthy) so no meta tag has
content: undefined. Ensure references remain to the same variables (title,
description, keywords, image) and preserve existing twitter card logic when
image is present.

---

Nitpick comments:
In `@examples/react/start-appwrite-basic/src/router.tsx`:
- Around line 1-17: The router currently created in getRouter() doesn't set a
default error component, so route-level errors won't be handled by your
DefaultCatchBoundary; update the createRouter call inside getRouter to pass
defaultErrorComponent: DefaultCatchBoundary (and optionally
defaultNotFoundComponent: DefaultCatchBoundary) after importing
DefaultCatchBoundary from src/components/DefaultCatchBoundary.tsx so the router
uses that component for uncaught route errors and 404s.

In `@examples/react/start-appwrite-basic/src/utils/appwrite.ts`:
- Around line 10-18: The code uses non-null assertions for Appwrite config
(process.env.APPWRITE_PROJECT_ID) in createSessionClient (and similarly where
APPWRITE_PROJECT_ID is used around lines 31-35); replace the `!` with runtime
validation: read process.env.APPWRITE_PROJECT_ID into a const, check if it is
undefined or empty, and throw a clear, descriptive Error (e.g.,
"APPWRITE_PROJECT_ID is required") before constructing the Client in
createSessionClient (and apply the same check where the variable is used
elsewhere) so TypeScript strictness is preserved and failures fail fast with
actionable messages.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a578314f-e1db-453d-9640-8da81811795e

📥 Commits

Reviewing files that changed from the base of the PR and between cbf9ecf and 080edcb.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (31)
  • docs/start/config.json
  • docs/start/framework/react/guide/authentication-overview.md
  • examples/react/start-appwrite-basic/.devcontainer/devcontainer.json
  • examples/react/start-appwrite-basic/.env
  • examples/react/start-appwrite-basic/.gitignore
  • examples/react/start-appwrite-basic/.prettierignore
  • examples/react/start-appwrite-basic/.vscode/settings.json
  • examples/react/start-appwrite-basic/README.md
  • examples/react/start-appwrite-basic/package.json
  • examples/react/start-appwrite-basic/src/components/Auth.tsx
  • examples/react/start-appwrite-basic/src/components/DefaultCatchBoundary.tsx
  • examples/react/start-appwrite-basic/src/components/Login.tsx
  • examples/react/start-appwrite-basic/src/components/NotFound.tsx
  • examples/react/start-appwrite-basic/src/hooks/useMutation.ts
  • examples/react/start-appwrite-basic/src/routeTree.gen.ts
  • examples/react/start-appwrite-basic/src/router.tsx
  • examples/react/start-appwrite-basic/src/routes/__root.tsx
  • examples/react/start-appwrite-basic/src/routes/_authed.tsx
  • examples/react/start-appwrite-basic/src/routes/_authed/posts.$postId.tsx
  • examples/react/start-appwrite-basic/src/routes/_authed/posts.index.tsx
  • examples/react/start-appwrite-basic/src/routes/_authed/posts.tsx
  • examples/react/start-appwrite-basic/src/routes/index.tsx
  • examples/react/start-appwrite-basic/src/routes/login.tsx
  • examples/react/start-appwrite-basic/src/routes/logout.tsx
  • examples/react/start-appwrite-basic/src/routes/signup.tsx
  • examples/react/start-appwrite-basic/src/styles/app.css
  • examples/react/start-appwrite-basic/src/utils/appwrite.ts
  • examples/react/start-appwrite-basic/src/utils/posts.ts
  • examples/react/start-appwrite-basic/src/utils/seo.ts
  • examples/react/start-appwrite-basic/tsconfig.json
  • examples/react/start-appwrite-basic/vite.config.ts

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

@coderabbitai coderabbitai Bot Apr 19, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Split the /public/build ignore rule from the Sentry comment.

Line 15 currently ignores a literal /public/build# Sentry Config File path, so /public/build is not covered.

Proposed fix
-/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
Verify each finding against the current code and only fix it if needed.

In `@examples/react/start-appwrite-basic/.gitignore` around lines 15 - 16, The
ignore line currently contains the literal "/public/build# Sentry Config File"
so the /public/build path isn't actually ignored; change the single line that
contains "/public/build# Sentry Config File" into two lines by separating the
path and the comment: add a line with "/public/build" and on the next line add
the comment beginning with "#" (e.g., "# Sentry Config File"), leaving the
existing ".env.sentry-build-plugin" entry unchanged.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Inherited verbatim from examples/react/start-supabase-basic/.gitignore. Keeping parity with the existing example here; happy to do a follow-up PR that fixes the malformed line in both.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@ChiragAgg5k, understood — keeping the two examples in sync makes sense. Would you like me to open a follow-up GitHub issue to track fixing the malformed /public/build# Sentry Config File line in both examples/react/start-supabase-basic/.gitignore and examples/react/start-appwrite-basic/.gitignore? That way it won't get lost.

Comment on lines +24 to +42
<label htmlFor="email" className="block text-xs">
Username
</label>
<input
type="email"
name="email"
id="email"
className="px-2 py-1 w-full rounded-sm border border-gray-500/20 bg-white dark:bg-gray-800"
/>
</div>
<div>
<label htmlFor="password" className="block text-xs">
Password
</label>
<input
type="password"
name="password"
id="password"
className="px-2 py-1 w-full rounded-sm border border-gray-500/20 bg-white dark:bg-gray-800"
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Apr 19, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Make the auth fields match email/password semantics.

Line 25 labels an email field as “Username”, and both fields allow empty submission. This makes the example easier to misuse and pushes avoidable validation errors to the server.

Suggested form field cleanup
             <label htmlFor="email" className="block text-xs">
-              Username
+              Email
             </label>
             <input
               type="email"
               name="email"
               id="email"
+              required
+              autoComplete="email"
               className="px-2 py-1 w-full rounded-sm border border-gray-500/20 bg-white dark:bg-gray-800"
             />
@@
             <input
               type="password"
               name="password"
               id="password"
+              required
               className="px-2 py-1 w-full rounded-sm border border-gray-500/20 bg-white dark:bg-gray-800"
             />
📝 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 htmlFor="email" className="block text-xs">
Username
</label>
<input
type="email"
name="email"
id="email"
className="px-2 py-1 w-full rounded-sm border border-gray-500/20 bg-white dark:bg-gray-800"
/>
</div>
<div>
<label htmlFor="password" className="block text-xs">
Password
</label>
<input
type="password"
name="password"
id="password"
className="px-2 py-1 w-full rounded-sm border border-gray-500/20 bg-white dark:bg-gray-800"
<label htmlFor="email" className="block text-xs">
Email
</label>
<input
type="email"
name="email"
id="email"
required
autoComplete="email"
className="px-2 py-1 w-full rounded-sm border border-gray-500/20 bg-white dark:bg-gray-800"
/>
</div>
<div>
<label htmlFor="password" className="block text-xs">
Password
</label>
<input
type="password"
name="password"
id="password"
required
className="px-2 py-1 w-full rounded-sm border border-gray-500/20 bg-white dark:bg-gray-800"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/react/start-appwrite-basic/src/components/Auth.tsx` around lines 24
- 42, Update the auth form in the Auth component so the fields match
email/password semantics: change the label text for the input with
id="email"/name="email" from "Username" to "Email", add required attributes to
both the email and password inputs (and consider adding autoComplete="email" and
autoComplete="current-password"), and add simple client-side validation (e.g.,
input type="email" already enforces format; ensure the password input has
required and a sensible minLength) on the inputs with id="email" and
id="password" so empty submissions are prevented.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Inherited verbatim from examples/react/start-supabase-basic/src/components/Auth.tsx. Keeping parity with the existing example (including the quirky "Username" label on an email input); happy to upgrade both in a follow-up PR if maintainers want the tighter form semantics everywhere.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@ChiragAgg5k That makes total sense — keeping the two examples structurally identical makes diffs between them much cleaner for learners. I'll defer to the maintainers on whether a follow-up cleanup pass across both examples is worthwhile.

Would you like me to open a GitHub issue to track the form-field improvements ("Email" label, required, autoComplete) for both start-supabase-basic and start-appwrite-basic so it doesn't get lost?

ʕ •ᴥ•ʔ


✏️ Learnings added
Learnt from: ChiragAgg5k
Repo: TanStack/router PR: 7225
File: examples/react/start-appwrite-basic/src/components/Auth.tsx:24-42
Timestamp: 2026-04-19T10:49:35.910Z
Learning: In the TanStack Router repository, `examples/react/start-appwrite-basic/src/components/Auth.tsx` is intentionally copied verbatim from `examples/react/start-supabase-basic/src/components/Auth.tsx` to maintain parity between the two auth examples. Any improvements (e.g. label text, `required`, `autoComplete` attributes) should be applied to both files together in a follow-up PR.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

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

const signupMutation = useMutation({
fn: useServerFn(signupFn),
})
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Apr 19, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

TanStack Start useServerFn when calling server functions from client components

💡 Result:

In TanStack Start, use useServerFn when calling server functions (created with createServerFn) from client components. This hook wraps the server function in a useCallback that handles server responses properly on the client, including automatic handling of errors, redirects (via throw redirect), and other server-specific responses that would not work correctly if calling the raw server function directly from client code. Example usage in a client component with TanStack Query: function PostList { const getPosts = useServerFn(getServerPosts) const { data } = useQuery({ queryKey: ['posts'], queryFn: => getPosts, }) // ... } Or with mutations: const addPlaylistFn = useMutation({ mutationFn: useServerFn(addPlaylist), }) Without useServerFn, redirects and certain error responses from server functions fail silently or incorrectly on the client. Server functions can be called directly from server contexts like route loaders without this hook.

Citations:


🏁 Script executed:

cat -n examples/react/start-appwrite-basic/src/components/Login.tsx | head -40

Repository: TanStack/router

Length of output: 1354


🏁 Script executed:

find examples/react/start-appwrite-basic/src/routes -name '*authed*' -o -name 'signup*' | head -10

Repository: TanStack/router

Length of output: 230


🏁 Script executed:

cat -n examples/react/start-appwrite-basic/src/routes/_authed.tsx

Repository: TanStack/router

Length of output: 1395


🏁 Script executed:

cat -n examples/react/start-appwrite-basic/src/routes/signup.tsx

Repository: TanStack/router

Length of output: 2480


Wrap loginFn with useServerFn—required for proper error and redirect handling.

Both loginFn and signupFn are server functions created with createServerFn. When calling server functions from client components in TanStack Start, useServerFn is required to properly handle server responses, including error states and redirects. signupFn correctly uses useServerFn (and relies on it due to the throw redirect() on line 36 of signup.tsx), but loginFn is called directly and bypasses this wrapper. Without it, error handling and server-side redirects fail silently or incorrectly on the client.

Fix
   const loginMutation = useMutation({
-    fn: loginFn,
+    fn: useServerFn(loginFn),
     onSuccess: async (ctx) => {
       if (!ctx.data?.error) {
         await router.invalidate()
         router.navigate({ to: '/' })
         return
       }
     },
   })
📝 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 signupMutation = useMutation({
fn: useServerFn(signupFn),
})
const loginMutation = useMutation({
fn: useServerFn(loginFn),
onSuccess: async (ctx) => {
if (!ctx.data?.error) {
await router.invalidate()
router.navigate({ to: '/' })
return
}
},
})
const signupMutation = useMutation({
fn: useServerFn(signupFn),
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/react/start-appwrite-basic/src/components/Login.tsx` around lines 11
- 24, The loginMutation currently calls loginFn directly which bypasses Start's
client wrapper—wrap loginFn with useServerFn (same pattern used for
signupMutation) so server responses, errors and redirects are handled correctly;
update the loginMutation's fn to useServerFn(loginFn) (keeping useMutation and
onSuccess logic intact) to match how signupMutation uses useServerFn.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

start-supabase-basic's Login passes loginFn directly too (fn: loginFn on its useMutation), and it works because the login server function does not throw redirect() — it only returns {error, message} and the component handles navigation itself via router.navigate in onSuccess. Keeping parity here; the signupFn path (which does throw redirect()) correctly uses useServerFn. Happy to upgrade both examples to wrap loginFn as well in a follow-up if maintainers prefer the stricter pattern.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@ChiragAgg5k, that's a fair and accurate clarification — thank you. Since loginFn only returns { error, message } and never throw redirect(), calling it directly is safe and the component correctly handles navigation client-side via router.navigate. useServerFn is the strict requirement only when the server function uses throw redirect() (as signupFn does). Parity with start-supabase-basic makes sense here. I'll withdraw the concern.


✏️ Learnings added
Learnt from: ChiragAgg5k
Repo: TanStack/router PR: 7225
File: examples/react/start-appwrite-basic/src/components/Login.tsx:11-24
Timestamp: 2026-04-19T10:49:38.189Z
Learning: In TanStack Start examples (e.g., `examples/react/start-appwrite-basic` and `examples/react/start-supabase-basic`), `useServerFn` is only required when a server function uses `throw redirect()` (e.g., `signupFn`). Server functions that only return plain data objects like `{ error: true, message }` (e.g., `loginFn` in `_authed.tsx`) can be passed directly to `useMutation` as `fn: loginFn` without `useServerFn`, and the component handles navigation itself via `router.navigate` in `onSuccess`. This is an intentional pattern maintained for parity between examples.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: hokkyss
Repo: TanStack/router PR: 6394
File: e2e/react-start/server-functions/src/routes/function-method/index.tsx:93-95
Timestamp: 2026-01-17T08:01:35.654Z
Learning: In TanStack Router e2e server function test files (e.g., files in `e2e/react-start/server-functions`, `e2e/solid-start/server-functions`, `e2e/vue-start/server-functions`), unhandled promise rejections in server function calls (e.g., `fn().then(setResult)` without `.catch()`) are intentional test behavior to make test failures visible in the test runner, consistent with existing patterns like in `e2e/react-start/server-functions/src/routes/factory/index.tsx`.

Comment thread examples/react/start-appwrite-basic/src/routes/__root.tsx Outdated
Comment thread examples/react/start-appwrite-basic/src/routes/__root.tsx Outdated
Comment thread examples/react/start-appwrite-basic/src/routes/signup.tsx Outdated
Comment thread examples/react/start-appwrite-basic/src/routes/signup.tsx
Comment on lines +27 to +29
.using-mouse * {
outline: none !important;
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Apr 19, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Preserve keyboard-visible focus styles.

Line 28 disables every outline under .using-mouse, which can hide focus indication if that class is still present during keyboard navigation. Scope this to non-keyboard focus states instead.

Suggested CSS adjustment
-  .using-mouse * {
-    outline: none !important;
+  .using-mouse *:focus:not(:focus-visible) {
+    outline: none;
   }
📝 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
.using-mouse * {
outline: none !important;
}
.using-mouse *:focus:not(:focus-visible) {
outline: none;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/react/start-appwrite-basic/src/styles/app.css` around lines 27 - 29,
The rule ".using-mouse * { outline: none !important; }" removes all focus
outlines even when keyboard users are navigating; change it to only remove
outline for focus states that are not keyboard-visible (e.g., target
".using-mouse *:focus:not(:focus-visible)" or the equivalent
:focus:not(:focus-visible) selector) so that keyboard-visible focus indicators
remain visible while mouse-only focus still hides outlines.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Inherited verbatim from examples/react/start-supabase-basic/src/styles/app.css. Keeping parity with the existing example; happy to do the :focus:not(:focus-visible) upgrade in a follow-up PR that covers both.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@ChiragAgg5k, that makes sense — keeping the two examples structurally identical is a good approach, and a single follow-up PR that upgrades both is the right way to handle it. I'll withdraw the concern for now.

Would you like me to open a GitHub issue to track the :focus:not(:focus-visible) upgrade for both start-appwrite-basic and start-supabase-basic?


✏️ Learnings added
Learnt from: ChiragAgg5k
Repo: TanStack/router PR: 7225
File: examples/react/start-appwrite-basic/src/styles/app.css:27-29
Timestamp: 2026-04-19T10:49:38.517Z
Learning: In `examples/react/start-appwrite-basic/src/styles/app.css` (and its counterpart `examples/react/start-supabase-basic/src/styles/app.css`), the `.using-mouse * { outline: none !important; }` rule is intentionally shared verbatim between the two example projects for parity. Any accessibility improvements (e.g., scoping to `:focus:not(:focus-visible)`) should be applied to both files in the same PR.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: hokkyss
Repo: TanStack/router PR: 5418
File: e2e/react-start/custom-identifier-prefix/src/styles/app.css:19-21
Timestamp: 2025-10-09T12:59:02.129Z
Learning: In e2e test directories (paths containing `e2e/`), accessibility concerns like outline suppression patterns are less critical since the code is for testing purposes, not production use.

Comment on lines +5 to +17
export type PostType = {
id: string
title: string
body: string
}

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)
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Apr 19, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Confirm JSONPlaceholder returns numeric post IDs.
# Expected: id_type is "number".
curl -s https://jsonplaceholder.typicode.com/posts/1 | jq '{id, id_type: (.id | type)}'

Repository: TanStack/router

Length of output: 95


🏁 Script executed:

cat -n examples/react/start-appwrite-basic/src/utils/posts.ts

Repository: TanStack/router

Length of output: 1331


Normalize JSONPlaceholder IDs before returning PostType.

Lines 16 and 34 assert the JSONPlaceholder response directly matches PostType, but the API returns numeric IDs while PostType.id is declared as string. This creates a runtime/type mismatch. Normalize the external payload instead of relying on the type assertion. Per coding guidelines, **/*.{ts,tsx}: Use TypeScript strict mode with extensive type safety.

Proposed fix
 export type PostType = {
   id: string
   title: string
   body: string
 }
+
+type JsonPlaceholderPost = Omit<PostType, 'id'> & {
+  id: number
+}
+
+function normalizePost(post: JsonPlaceholderPost): PostType {
+  return {
+    ...post,
+    id: String(post.id),
+  }
+}
 
 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)
+      .get<JsonPlaceholderPost>(
+        `https://jsonplaceholder.typicode.com/posts/${postId}`,
+      )
+      .then((r) => normalizePost(r.data))
       .catch((err) => {
         console.error(err)
         if (err.status === 404) {
@@
     return axios
-      .get<Array<PostType>>('https://jsonplaceholder.typicode.com/posts')
-      .then((r) => r.data.slice(0, 10))
+      .get<Array<JsonPlaceholderPost>>(
+        'https://jsonplaceholder.typicode.com/posts',
+      )
+      .then((r) => r.data.slice(0, 10).map(normalizePost))
   },
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/react/start-appwrite-basic/src/utils/posts.ts` around lines 5 - 17,
The handler in fetchPost currently assumes the external JSONPlaceholder payload
matches PostType (id:string) but the API returns id as a number; modify the
fetch to parse/normalize the response before returning: call axios.get with an
appropriate external response shape (e.g., id:number, title:string, body:string)
or use any, then map the received object to PostType by converting id to a
string (e.g., id: String(response.id)) and copying title/body; remove the
incorrect generic <PostType> on axios.get and ensure the handler returns the
normalized PostType object.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Inherited verbatim from examples/react/start-supabase-basic/src/utils/posts.ts. The {id: String} template-literal interpolation of post.id still works at runtime in the <Link params> path and there is no current consumer that relies on strict equality against the typed string, so this has not surfaced as a real bug. Keeping parity with the existing example; happy to normalize both in a follow-up PR.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@ChiragAgg5k, that's a fair point — maintaining parity with start-supabase-basic is a reasonable constraint here, and JavaScript's implicit coercion in template literals does make the numeric-vs-string mismatch benign at runtime. I'll note this for future reviews of similar example files.

Would you like me to open a follow-up GitHub issue to track normalizing PostType.id in both start-appwrite-basic and start-supabase-basic together?

(´• ω •`) 🐇


🧠 Learnings used
Learnt from: hokkyss
Repo: TanStack/router PR: 6394
File: e2e/react-start/server-functions/src/routes/function-method/index.tsx:93-95
Timestamp: 2026-01-17T08:01:35.654Z
Learning: In TanStack Router e2e server function test files (e.g., files in `e2e/react-start/server-functions`, `e2e/solid-start/server-functions`, `e2e/vue-start/server-functions`), unhandled promise rejections in server function calls (e.g., `fn().then(setResult)` without `.catch()`) are intentional test behavior to make test failures visible in the test runner, consistent with existing patterns like in `e2e/react-start/server-functions/src/routes/factory/index.tsx`.

Learnt from: nlynzaad
Repo: TanStack/router PR: 6389
File: e2e/solid-start/basic/src/routes/specialChars/malformed/route.tsx:11-24
Timestamp: 2026-01-18T17:36:24.388Z
Learning: In TanStack Router e2e malformed test routes (e.g., `e2e/solid-start/basic/src/routes/specialChars/malformed/route.tsx`), intentional use of `href` prop instead of `to` prop on Link components may be used for testing purposes to validate specific encoding/decoding behaviors or edge cases, even though the standard `tanstack/solid-router` Link API uses the `to` prop.

Comment on lines +12 to +30
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 },
]
: []),
]
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Apr 19, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Open Graph protocol meta tags property attribute og:title og:image

💡 Result:

The Open Graph (OG) protocol uses meta tags with the attribute name="property" and values prefixed with "og:", such as property="og:title" and property="og:image". These are placed in the section of HTML pages to control how content appears when shared on social media platforms like Facebook, LinkedIn, and others. og:title: Specifies the title of the page or object as it should appear in the social graph preview. Example: . It should be concise, ideally under 60-75 characters. og:image: Specifies the URL of an image to represent the object in the preview. Recommended size is 1200x630 pixels (1.91:1 aspect ratio) for optimal display. Supports structured properties like og:image:width, og:image:height, og:image:alt, og:image:type (e.g., image/jpeg), and og:image:secure_url for HTTPS. Example: . These are part of the four required basic OG tags (along with og:type and og:url), as defined in the official specification. Facebook's documentation confirms the property attribute usage and provides additional guidance on images.

Citations:


🏁 Script executed:

cat -n examples/react/start-appwrite-basic/src/utils/seo.ts

Repository: TanStack/router

Length of output: 1174


🏁 Script executed:

cd examples/react/start-appwrite-basic && grep -r "seo(" --include="*.ts" --include="*.tsx" | head -20

Repository: TanStack/router

Length of output: 95


🏁 Script executed:

cd examples/react/start-appwrite-basic && grep -r "from.*seo" --include="*.ts" --include="*.tsx"

Repository: TanStack/router

Length of output: 115


🏁 Script executed:

sed -n '1,100p' examples/react/start-appwrite-basic/src/routes/__root.tsx

Repository: TanStack/router

Length of output: 2384


🏁 Script executed:

cd examples/react/start-appwrite-basic && find . -name "*.md" -o -name "*.json" | grep -i "tanstack\|router" | head -10

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

grep -r "HeadContent\|head:" examples/react/start-appwrite-basic/src --include="*.ts" --include="*.tsx" -A 5 | head -40

Repository: TanStack/router

Length of output: 1418


Use property for Open Graph tags and conditionally emit optional meta fields.

Open Graph meta tags (lines 20-22, 27) must use property="og:*" instead of name="og:*" per the Open Graph protocol specification. Additionally, optional fields (description, keywords) should be conditionally emitted to avoid creating meta tags with content: undefined.

Suggested metadata cleanup
   const tags = [
     { title },
-    { name: 'description', content: description },
-    { name: 'keywords', content: keywords },
+    ...(description ? [{ name: 'description', content: description }] : []),
+    ...(keywords ? [{ name: 'keywords', content: keywords }] : []),
     { name: 'twitter:title', content: title },
-    { name: 'twitter:description', content: description },
+    ...(description
+      ? [{ 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 },
+    { 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' },
-          { name: 'og:image', content: image },
+          { property: 'og:image', content: image },
         ]
       : []),
   ]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/react/start-appwrite-basic/src/utils/seo.ts` around lines 12 - 30,
The tags array in seo.ts currently uses name="og:*" and always emits
description/keywords which can be undefined; update the construction of the tags
array (the tags constant that references title, description, keywords, image)
to: 1) use property: 'og:*' for Open Graph entries (e.g., property: 'og:title',
property: 'og:description', property: 'og:type', property: 'og:image') instead
of name, and 2) conditionally push meta objects only when their values exist
(guard emitting description, keywords, twitter:* and og:image when
description/keywords/image are truthy) so no meta tag has content: undefined.
Ensure references remain to the same variables (title, description, keywords,
image) and preserve existing twitter card logic when image is present.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Inherited verbatim from examples/react/start-supabase-basic/src/utils/seo.ts. Keeping parity with the existing example; happy to do the property: 'og:*' + conditional-emit upgrade in a follow-up PR that covers both.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@ChiragAgg5k, that makes perfect sense — keeping the Appwrite and Supabase examples in lockstep is a good call. Fixing both in one follow-up PR is cleaner than having them diverge. Would you like me to open a tracking GitHub issue for the property: 'og:*' + conditional-emit cleanup so it doesn't fall through the cracks?


✏️ Learnings added
Learnt from: ChiragAgg5k
Repo: TanStack/router PR: 7225
File: examples/react/start-appwrite-basic/src/utils/seo.ts:12-30
Timestamp: 2026-04-19T10:49:42.382Z
Learning: In the TanStack Router repository, `examples/react/start-appwrite-basic/src/utils/seo.ts` is intentionally copied verbatim from `examples/react/start-supabase-basic/src/utils/seo.ts` to maintain parity between the two examples. Any fixes (e.g., using `property: 'og:*'` for Open Graph tags, conditionally emitting optional meta fields) should be applied to both files together in the same PR rather than fixing one in isolation.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

- __root.tsx: drop invalid 5-digit hex `#fffff` and the misplaced `color`
  attr from the manifest link; add `lang="en"` to the root `<html>` for
  screen-reader / a11y audits
- signup.tsx: reorder imports to satisfy ESLint `sort-imports`
- signup.tsx: on `createEmailPasswordSession` failure after a successful
  `account.create`, roll back the newly created user via the admin Users
  service so retries don't hit `user_already_exists` (Appwrite-specific —
  the two-call pattern is only in this example, not in start-supabase-basic)
- appwrite.ts: add `createAdminUsers()` helper so the signup handler can
  perform that rollback with the same credentials as `createAdminClient`
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.

1 participant