Update mintlify docs in various spots throughout. (#1305)

<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->
This commit is contained in:
Madison 2026-04-02 11:55:27 -05:00 committed by GitHub
parent 13fccd32b6
commit 3fa081f1e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 258 additions and 68 deletions

View File

@ -57,32 +57,43 @@
"group": "Getting Started",
"pages": [
"docs/getting-started/setup",
"docs/getting-started/components",
"docs/getting-started/users",
"docs/getting-started/components",
"docs/getting-started/production",
"docs/getting-started/vite-example"
]
},
{
"group": "Apps",
"pages": [
"docs/apps/api-keys",
"docs/apps/emails",
"docs/apps/oauth",
"docs/apps/orgs-and-teams",
"docs/apps/permissions",
"docs/apps/webhooks",
"docs/apps/payments",
"docs/apps/analytics"
]
},
{
"group": "Concepts",
"group": "Going Further",
"pages": [
"docs/going-further/working-with-ai",
"docs/concepts/stack-app",
"docs/concepts/backend-integration",
"docs/going-further/local-development",
{
"group": "Component Customization",
"pages": [
"docs/customization/custom-pages",
"docs/customization/custom-styles",
"docs/customization/dark-mode",
"docs/customization/internationalization",
{
"group": "Page Examples",
"pages": [
"docs/customization/page-examples",
"docs/customization/page-examples/forgot-password",
"docs/customization/page-examples/password-reset",
"docs/customization/page-examples/sign-in",
"docs/customization/page-examples/sign-up"
]
}
]
},
"docs/concepts/custom-user-data",
"docs/concepts/sign-up-rules",
"docs/concepts/jwt",
"docs/concepts/user-onboarding",
"docs/concepts/team-selection",
{
"group": "Auth Providers",
"pages": [
@ -102,39 +113,45 @@
"docs/concepts/auth-providers/two-factor-auth",
"docs/concepts/auth-providers/x-twitter"
]
},
"docs/concepts/stack-app",
"docs/concepts/team-selection",
"docs/concepts/user-onboarding"
}
]
},
{
"group": "Customization",
"group": "Apps",
"pages": [
"docs/customization/custom-pages",
"docs/customization/custom-styles",
"docs/customization/dark-mode",
"docs/customization/internationalization",
{
"group": "Page Examples",
"group": "Authentication",
"icon": "lock",
"pages": [
"docs/customization/page-examples",
"docs/customization/page-examples/forgot-password",
"docs/customization/page-examples/password-reset",
"docs/customization/page-examples/sign-in",
"docs/customization/page-examples/sign-up"
"docs/apps/auth-providers",
"docs/apps/oauth"
]
}
},
"docs/apps/emails",
"docs/apps/payments",
"docs/apps/analytics",
"docs/apps/api-keys",
"docs/apps/data-vault",
"docs/apps/launch-checklist",
"docs/apps/permissions",
"docs/apps/orgs-and-teams",
"docs/apps/webhooks"
]
},
{
"group": "Integrations",
"pages": [
"docs/others/supabase",
"docs/others/convex",
"docs/others/mcp-setup"
]
},
{
"group": "Other",
"pages": [
"docs/others/cli-authentication",
"docs/others/self-host",
"docs/others/supabase",
"docs/others/convex",
"docs/others/mcp-setup"
"docs/others/cli-authentication",
"docs/going-further/showcase"
]
}
]

View File

@ -1,7 +1,6 @@
---
title: Auth Providers
description: Configure authentication providers for your application
icon: shield
---
Stack Auth supports a variety of authentication providers to give your users flexible sign-in options. You can configure these providers through the Stack Auth dashboard.

View File

@ -0,0 +1,7 @@
---
title: "Data Vault"
description: "Securely store and manage sensitive data for your application"
icon: "vault"
---
This page is under construction. Check back soon for documentation on using the Data Vault.

View File

@ -1,7 +1,7 @@
---
title: "Emails"
description: "Send custom emails to your users with Stack Auth's email system."
icon: "mail"
icon: "envelope"
---
Stack Auth provides emails that allows you to send custom emails to your users. The system supports both custom HTML emails and template-based emails with theming.

View File

@ -0,0 +1,7 @@
---
title: "Launch Checklist"
description: "Everything you need to verify before going live with Stack Auth"
icon: "clipboard-check"
---
This page is under construction. Check back soon for the launch checklist.

View File

@ -1,7 +1,6 @@
---
title: "OAuth"
description: "Managing third-party OAuth access tokens"
icon: "globe"
---
Stack has good support for working with OAuth and OIDC providers, such as Google, Facebook, Microsoft, and others.

View File

@ -1,54 +1,72 @@
---
title: Users
title: User Fundamentals
description: Access and manage user information within custom components
sidebarTitle: Users
sidebarTitle: User Fundamentals
icon: "users"
---
import { UserFieldsTable } from "/snippets/user-fields-table.jsx";
# Users
You will inevitably build custom components that access the user in one way or another. In this section, we will take a closer look at the functions and hooks that let you do this.
You've set up Stack Auth. Now let's understand the most important object in your application: the **User**.
## Client Component basics
The user object represents whoever is currently interacting with your app — their identity, profile, and metadata. Almost everything you build will revolve around it: retrieving the current user, protecting pages from unauthorized access, updating profile information, signing out, and more.
The `useUser()` hook returns the current user in a Client Component. By default, it will return `null` if the user is not signed in.
## Getting the current user
```tsx title="my-client-component.tsx"
"use client";
import { useUser } from "@stackframe/stack"
The way you retrieve the current user depends on whether you're in a Client Component or a Server Component. Both return `null` if the user is not signed in.
export function MyClientComponent() {
const user = useUser();
return <div>{user ? `Hello, ${user.displayName ?? "anon"}` : 'You are not logged in'}</div>;
}
```
<Tabs>
<Tab title="Client Component">
Use the `useUser()` hook:
The `useUser()` hook is simply a shorthand for `useStackApp().useUser()`. `useStackApp()` also contains other useful hooks and methods for clients, which will be described later.
```tsx title="my-client-component.tsx"
"use client";
import { useUser } from "@stackframe/stack"
Sometimes, you want to retrieve the user only if they're signed in, and redirect to the sign-in page otherwise. In this case, simply pass `{ or: "redirect" }`, and the function will never return `null`.
export function MyClientComponent() {
const user = useUser();
return <div>{user ? `Hello, ${user.displayName ?? "anon"}` : 'You are not logged in'}</div>;
}
```
```tsx
const user = useUser({ or: "redirect" });
return <div>{`Hello, ${user.displayName ?? "anon"}`}</div>;
```
The `useUser()` hook is simply a shorthand for `useStackApp().useUser()`. `useStackApp()` also contains other useful hooks and methods for clients, which will be described later. Since it's a React hook, your component will automatically re-render when the user changes (e.g. on sign-out).
</Tab>
<Tab title="Server Component">
Since `useUser()` is a stateful hook, you can't use it in Server Components. Instead, import `stackServerApp` from `stack/server.ts` and call `getUser()`:
## Server Component basics
```tsx title="my-server-component.tsx"
import { stackServerApp } from "@/stack/server";
Since `useUser()` is a stateful hook, you can't use it on server components. Instead, you can import `stackServerApp` from `stack/server.ts` and call `getUser()`:
export default async function MyServerComponent() {
const user = await stackServerApp.getUser();
return <div>{user ? `Hello, ${user.displayName ?? "anon"}` : 'You are not logged in'}</div>;
}
```
```tsx title="my-server-component.tsx"
import { stackServerApp } from "@/stack/server";
export default async function MyServerComponent() {
const user = await stackServerApp.getUser(); // or: stackServerApp.getUser({ or: "redirect" })
return <div>{user ? `Hello, ${user.displayName ?? "anon"}` : 'You are not logged in'}</div>;
}
```
Unlike `useUser()`, `getUser()` fetches the user once at request time and does not re-render on changes. You can also call `useStackApp().getUser()` on the client side to get the user in a non-component context.
</Tab>
</Tabs>
<Info>
Since `useUser()` is a hook, it will re-render the component on user changes (eg. signout), while `getUser()` will only fetch the user once (on page load). You can also call `useStackApp().getUser()` on the client side to get the user in a non-component context.
</Info>
### Requiring a signed-in user
Sometimes, you want to retrieve the user only if they're signed in, and redirect to the sign-in page otherwise. In this case, simply pass `{ or: "redirect" }`, and the function will never return `null`.
You can also use `{ or: "throw" }` to throw an error instead — useful in API routes and server actions where a redirect doesn't make sense.
In both cases, the return type is non-nullable, so you don't need to handle `null`.
```tsx
const user = useUser({ or: "redirect" });
// user is guaranteed to be non-null here
return <div>{`Hello, ${user.displayName ?? "anon"}`}</div>;
```
## Protecting a page
There are three ways to protect a page: in Client Components with `useUser({ or: "redirect" })`, in Server Components with `await getUser({ or: "redirect" })`, or with middleware.
@ -116,6 +134,16 @@ Middleware can be used whenever it is easy to tell whether a page should be prot
## User data
### What's on a user object?
Before diving into updates, here's an overview of the most important fields available on every user:
<UserFieldsTable />
For the full list of fields and methods, see the [User SDK reference](../sdk/types/user).
### Updating a user
You can update attributes on a user object with the `user.update()` function.
```tsx title="my-client-component.tsx"
@ -130,7 +158,52 @@ export default function MyClientComponent() {
}
```
You can also store custom user data in the `clientMetadata`, `serverMetadata`, or `clientReadOnlyMetadata` fields. More information [here](../concepts/custom-user-data).
### Custom metadata
Beyond built-in fields like `displayName` and `primaryEmail`, you'll often need to store your own data on a user. Stack Auth provides three metadata fields for this:
- **`clientMetadata`** — Readable and writable from both the client and the server. Use this for non-sensitive user preferences like theme, locale, or UI state.
- **`serverMetadata`** — Readable and writable only from the server. Use this for sensitive or internal data like Stripe customer IDs, internal flags, or anything users shouldn't be able to see or modify.
- **`clientReadOnlyMetadata`** — Readable from the client, writable only from the server. Use this for data the client needs to display but shouldn't be able to change, like subscription plans or role labels.
For example, storing a user's theme preference in `clientMetadata`:
```tsx title="theme-toggle.tsx"
'use client';
import { useUser } from "@stackframe/stack";
export default function ThemeToggle() {
const user = useUser({ or: "redirect" });
const currentTheme = user.clientMetadata?.theme ?? "light";
return (
<button onClick={async () => {
await user.update({
clientMetadata: {
...user.clientMetadata,
theme: currentTheme === "light" ? "dark" : "light",
},
});
}}>
Switch to {currentTheme === "light" ? "dark" : "light"} mode
</button>
);
}
```
And storing sensitive data in `serverMetadata` (server-side only):
```tsx title="server-example.ts"
const user = await stackServerApp.getUser({ or: "throw" });
await user.update({
serverMetadata: {
...user.serverMetadata,
stripeCustomerId: "cus_abc123",
},
});
```
For more details, see the [Custom User Data](../concepts/custom-user-data) documentation.
## Signing out
@ -228,6 +301,37 @@ After saving your code, you can see the profile page on [http://localhost:3000/p
For more examples on how to use the `User` object, check the [the SDK documentation](../sdk/types/user).
## Anonymous users
Stack Auth supports anonymous users - users who can interact with your app without signing up. This is useful for features like guest checkouts, try-before-you-sign-up flows, or collecting analytics before a user creates an account.
To get an anonymous user, pass `{ or: "anonymous" }` to `useUser()` or `getUser()`. If the current visitor isn't signed in, Stack will automatically create an anonymous account for them behind the scenes.
```tsx title="my-client-component.tsx"
"use client";
import { useUser } from "@stackframe/stack";
export default function MyComponent() {
const user = useUser({ or: "anonymous" });
// user is always non-null — either a real user or an anonymous one
return <div>Your user ID: {user.id}</div>;
}
```
Anonymous users have an `isAnonymous` property set to `true` and are also considered restricted (`isRestricted` is `true`). You can check this to show different UI for anonymous vs. signed-in users:
```tsx
const user = useUser({ or: "anonymous" });
if (user.isAnonymous) {
return <div>You're browsing as a guest. <a href="/handler/sign-up">Create an account</a> to save your progress.</div>;
}
return <div>Welcome back, {user.displayName}!</div>;
```
<Info>
When using `{ or: "anonymous" }`, the `includeRestricted` option is automatically set to `true`, since all anonymous users are restricted by definition. You cannot use `{ or: "anonymous" }` with `{ includeRestricted: false }`.
</Info>
## Next steps
In the next guide, we will show you how to put [your application into production](./production).

View File

@ -0,0 +1,6 @@
---
title: "Local Development"
description: "Set up and run Stack Auth locally for development and testing"
---
This page is under construction. Check back soon for instructions on setting up Stack Auth for local development.

View File

@ -0,0 +1,6 @@
---
title: "Showcase"
description: "See what others have built with Stack Auth"
---
This page is under construction. Check back soon for a showcase of projects and applications built with Stack Auth.

View File

@ -0,0 +1,6 @@
---
title: "Working with AI"
description: "Use AI-powered tools and integrations with Stack Auth to accelerate your development workflow"
---
This page is under construction. Check back soon for guidance on integrating Stack Auth with AI-powered development tools.

View File

@ -0,0 +1,39 @@
export const UserFieldsTable = () => {
const fields = [
{ name: "id", type: "string", description: "Unique identifier — always use this for lookups, not email" },
{ name: "displayName", type: "string | null", description: "The user's display name" },
{ name: "primaryEmail", type: "string | null", description: "The user's email address (not guaranteed unique)" },
{ name: "primaryEmailVerified", type: "boolean", description: "Whether the email has been verified" },
{ name: "profileImageUrl", type: "string | null", description: "URL to the user's profile image" },
{ name: "signedUpAt", type: "Date", description: "When the user created their account" },
{ name: "clientMetadata", type: "any", description: "Custom data, readable/writable from client and server" },
{ name: "clientReadOnlyMetadata", type: "any", description: "Custom data, readable from client, writable only from server" },
{ name: "serverMetadata", type: "any", description: "Custom data, server-only (only on server-side user objects)" },
{ name: "hasPassword", type: "boolean", description: "Whether the user has a password set" },
{ name: "isAnonymous", type: "boolean", description: "Whether this is an anonymous user" },
{ name: "isRestricted", type: "boolean", description: "Hasn't completed onboarding requirements (e.g. email not verified)" },
];
return (
<div className="not-prose my-6 overflow-hidden rounded-2xl border border-zinc-950/10 dark:border-white/10">
<div className="border-b border-zinc-950/10 bg-zinc-950/[0.03] px-4 py-3 text-sm font-medium text-zinc-950/80 dark:border-white/10 dark:bg-white/[0.03] dark:text-white/80">
User object fields
</div>
<div className="divide-y divide-zinc-950/[0.06] dark:divide-white/[0.06]">
{fields.map((field) => (
<div key={field.name} className="flex items-baseline gap-4 px-4 py-2.5">
<code className="shrink-0 text-[13px] font-semibold text-zinc-950 dark:text-white">
{field.name}
</code>
<code className="shrink-0 text-[12px] text-zinc-500 dark:text-zinc-400">
{field.type}
</code>
<span className="text-[13px] leading-snug text-zinc-600 dark:text-zinc-400">
{field.description}
</span>
</div>
))}
</div>
</div>
);
};