diff --git a/docs-mintlify/docs.json b/docs-mintlify/docs.json index 6efc41da1c..461747cbd4 100644 --- a/docs-mintlify/docs.json +++ b/docs-mintlify/docs.json @@ -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,40 +113,46 @@ "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": "Other", + "group": "Integrations", "pages": [ - "docs/others/cli-authentication", - "docs/others/self-host", "docs/others/supabase", "docs/others/convex", "docs/others/mcp-setup" ] + }, + { + "group": "Other", + "pages": [ + "docs/others/self-host", + "docs/others/cli-authentication", + "docs/going-further/showcase" + ] } ] }, diff --git a/docs-mintlify/docs/apps/auth-providers.mdx b/docs-mintlify/docs/apps/auth-providers.mdx index 2ccd61180a..0812c42a0c 100644 --- a/docs-mintlify/docs/apps/auth-providers.mdx +++ b/docs-mintlify/docs/apps/auth-providers.mdx @@ -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. diff --git a/docs-mintlify/docs/apps/data-vault.mdx b/docs-mintlify/docs/apps/data-vault.mdx new file mode 100644 index 0000000000..45e7b1e605 --- /dev/null +++ b/docs-mintlify/docs/apps/data-vault.mdx @@ -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. diff --git a/docs-mintlify/docs/apps/emails.mdx b/docs-mintlify/docs/apps/emails.mdx index b1e75a120c..ed3466e9d8 100644 --- a/docs-mintlify/docs/apps/emails.mdx +++ b/docs-mintlify/docs/apps/emails.mdx @@ -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. diff --git a/docs-mintlify/docs/apps/launch-checklist.mdx b/docs-mintlify/docs/apps/launch-checklist.mdx new file mode 100644 index 0000000000..60ac7d5ac1 --- /dev/null +++ b/docs-mintlify/docs/apps/launch-checklist.mdx @@ -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. diff --git a/docs-mintlify/docs/apps/oauth.mdx b/docs-mintlify/docs/apps/oauth.mdx index 529845a005..f3160561bb 100644 --- a/docs-mintlify/docs/apps/oauth.mdx +++ b/docs-mintlify/docs/apps/oauth.mdx @@ -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. diff --git a/docs-mintlify/docs/getting-started/users.mdx b/docs-mintlify/docs/getting-started/users.mdx index 57106a31b2..f34cb61ca5 100644 --- a/docs-mintlify/docs/getting-started/users.mdx +++ b/docs-mintlify/docs/getting-started/users.mdx @@ -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" - -export function MyClientComponent() { - const user = useUser(); - return
{user ? `Hello, ${user.displayName ?? "anon"}` : 'You are not logged in'}
; -} -``` +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. -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. + + + Use the `useUser()` hook: -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`. + ```tsx title="my-client-component.tsx" + "use client"; + import { useUser } from "@stackframe/stack" -```tsx -const user = useUser({ or: "redirect" }); -return
{`Hello, ${user.displayName ?? "anon"}`}
; -``` + export function MyClientComponent() { + const user = useUser(); + return
{user ? `Hello, ${user.displayName ?? "anon"}` : 'You are not logged in'}
; + } + ``` -## Server Component basics + 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). +
+ + Since `useUser()` is a stateful hook, you can't use it in Server Components. Instead, import `stackServerApp` from `stack/server.ts` and call `getUser()`: -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()`: + ```tsx title="my-server-component.tsx" + import { stackServerApp } from "@/stack/server"; -```tsx title="my-server-component.tsx" -import { stackServerApp } from "@/stack/server"; + export default async function MyServerComponent() { + const user = await stackServerApp.getUser(); + return
{user ? `Hello, ${user.displayName ?? "anon"}` : 'You are not logged in'}
; + } + ``` -export default async function MyServerComponent() { - const user = await stackServerApp.getUser(); // or: stackServerApp.getUser({ or: "redirect" }) - return
{user ? `Hello, ${user.displayName ?? "anon"}` : 'You are not logged in'}
; -} -``` + 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. +
+
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. +### 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
{`Hello, ${user.displayName ?? "anon"}`}
; +``` + ## 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: + + + +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 ( + + ); +} +``` + +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
Your user ID: {user.id}
; +} +``` + +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
You're browsing as a guest. Create an account to save your progress.
; +} +return
Welcome back, {user.displayName}!
; +``` + + + 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 }`. + + ## Next steps In the next guide, we will show you how to put [your application into production](./production). diff --git a/docs-mintlify/docs/going-further/local-development.mdx b/docs-mintlify/docs/going-further/local-development.mdx new file mode 100644 index 0000000000..58f3294398 --- /dev/null +++ b/docs-mintlify/docs/going-further/local-development.mdx @@ -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. diff --git a/docs-mintlify/docs/going-further/showcase.mdx b/docs-mintlify/docs/going-further/showcase.mdx new file mode 100644 index 0000000000..1ea3af6c1e --- /dev/null +++ b/docs-mintlify/docs/going-further/showcase.mdx @@ -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. diff --git a/docs-mintlify/docs/going-further/working-with-ai.mdx b/docs-mintlify/docs/going-further/working-with-ai.mdx new file mode 100644 index 0000000000..ef5c7a43d9 --- /dev/null +++ b/docs-mintlify/docs/going-further/working-with-ai.mdx @@ -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. diff --git a/docs-mintlify/snippets/user-fields-table.jsx b/docs-mintlify/snippets/user-fields-table.jsx new file mode 100644 index 0000000000..a81784815f --- /dev/null +++ b/docs-mintlify/snippets/user-fields-table.jsx @@ -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 ( +
+
+ User object fields +
+
+ {fields.map((field) => ( +
+ + {field.name} + + + {field.type} + + + {field.description} + +
+ ))} +
+
+ ); +};