;
+// }
+
+// --------------------------------------------
+
+// import { useStackApp } from "@stackframe/stack";
+// import { useState } from "react";
+
+// export default function CustomCredentialSignIn() {
+// const [email, setEmail] = useState('');
+// const [password, setPassword] = useState('');
+// const [error, setError] = useState('');
+// const app = useStackApp();
+
+// const onSubmit = async () => {
+// if (!password) {
+// setError('Please enter your password');
+// return;
+// }
+// // this will redirect to app.urls.afterSignIn if successful, you can customize it in the StackServerApp constructor
+// const errorCode = await app.signInWithCredential({ email, password });
+// // It is better to handle each error code separately, but we will just show the error code directly for simplicity here
+// if (errorCode) {
+// setError(errorCode.message);
+// }
+// };
+
+// return (
+//
+// );
+// }
+
+// --------------------------------------------
+
+
+import { useStackApp } from "@stackframe/stack";
+import { useState } from "react";
+
+export default function CustomCredentialSignIn() {
+ const [email, setEmail] = useState('');
+ const [error, setError] = useState('');
+ const [message, setMessage] = useState('');
+ const app = useStackApp();
+
+ const onSubmit = async () => {
+ // this will redirect to app.urls.afterSignIn if successful, you can customize it in the StackServerApp constructor
+ const errorCode = await app.sendMagicLinkEmail(email);
+ // It is better to handle each error code separately, but we will just show the error code directly for simplicity here
+ if (errorCode) {
+ setError(errorCode.message);
+ } else {
+ setMessage('Magic link sent! Please check your email.');
+ }
+ };
+
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/apps/custom-pages-example/src/app/signup/page.tsx b/apps/custom-pages-example/src/app/signup/page.tsx
new file mode 100644
index 0000000000..d519d495ea
--- /dev/null
+++ b/apps/custom-pages-example/src/app/signup/page.tsx
@@ -0,0 +1,43 @@
+'use client';
+
+
+// import { SignUp } from "@stackframe/stack";
+
+// export default function DefaultSignUp() {
+// return ;
+// }
+
+// --------------------------------------------
+
+
+import { useStackApp } from "@stackframe/stack";
+import { useState } from "react";
+
+export default function CustomCredentialSignUp() {
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+ const [error, setError] = useState('');
+ const app = useStackApp();
+
+ const onSubmit = async () => {
+ if (!password) {
+ setError('Please enter your password');
+ return;
+ }
+ // this will redirect to app.urls.afterSignUp if successful, you can customize it in the StackServerApp constructor
+ const errorCode = await app.signUpWithCredential({ email, password });
+ // It is better to handle each error code separately, but we will just show the error code directly for simplicity here
+ if (errorCode) {
+ setError(errorCode.message);
+ }
+ };
+
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/apps/custom-pages-example/src/components/provider.tsx b/apps/custom-pages-example/src/components/provider.tsx
new file mode 100644
index 0000000000..21d5444a2d
--- /dev/null
+++ b/apps/custom-pages-example/src/components/provider.tsx
@@ -0,0 +1,10 @@
+'use client';;
+import { StackTheme } from "@stackframe/stack";
+
+export default function Provider({ children }) {
+ return (
+
+ {children}
+
+ );
+}
\ No newline at end of file
diff --git a/apps/custom-pages-example/src/stack.tsx b/apps/custom-pages-example/src/stack.tsx
new file mode 100644
index 0000000000..e5279bcd4a
--- /dev/null
+++ b/apps/custom-pages-example/src/stack.tsx
@@ -0,0 +1,11 @@
+import "server-only";
+
+import { StackServerApp } from "@stackframe/stack";
+
+export const stackServerApp = new StackServerApp({
+ tokenStore: "nextjs-cookie",
+ urls: {
+ signIn: "/signin",
+ signUp: "/signup",
+ }
+});
diff --git a/apps/custom-pages-example/tailwind.config.js b/apps/custom-pages-example/tailwind.config.js
new file mode 100644
index 0000000000..902463811b
--- /dev/null
+++ b/apps/custom-pages-example/tailwind.config.js
@@ -0,0 +1,8 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ content: ["./src/**/*.{js,ts,jsx,tsx,mdx}"],
+ theme: {
+ extend: {},
+ },
+ plugins: [],
+};
diff --git a/apps/custom-pages-example/tsconfig.json b/apps/custom-pages-example/tsconfig.json
new file mode 100644
index 0000000000..71d2573b5a
--- /dev/null
+++ b/apps/custom-pages-example/tsconfig.json
@@ -0,0 +1,42 @@
+{
+ "compiler": {
+ "styledComponents": true,
+ },
+ "compilerOptions": {
+ "target": "esnext",
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
+ "allowJs": true,
+ "strict": false,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "noErrorTruncation": true,
+ "module": "esnext",
+ "moduleResolution": "Bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "incremental": true,
+ "jsx": "preserve",
+ "baseUrl": ".",
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "strictNullChecks": true,
+ "skipLibCheck": true
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
+}
diff --git a/docs/docs/01-getting-started/01-why-stack.md b/docs/docs/01-getting-started/01-why-stack.md
new file mode 100644
index 0000000000..c995c5f91d
--- /dev/null
+++ b/docs/docs/01-getting-started/01-why-stack.md
@@ -0,0 +1,59 @@
+---
+---
+
+# Why Stack?
+
+You might wonder: "With so many authentication libraries available, why create Stack?"
+
+The answer is straightforward: **They all suck at developer experience**.
+
+As developers, we don't want to be bothered by decisions about using JWT or session tokens, handling token refreshes, or managing user data synchronization with frontend states. **Auth should be a 5 minutes job**, not 5 days. Our focus should be on building the core product.
+
+Stack abstract all these complexities away by deeply integrate into the frontend and backend framework. It offers a simple and intuitive interface, while remains highly flexible, customizable, and powerful.
+
+Here is an example. To use the current user, simply call:
+
+```tsx
+export function MyComponent() {
+ const user = useUser();
+ return
{user ? `Hi, ${user.displayName}` : 'You are not logged in'}
;
+}
+```
+
+That's it! You don't need to worry about fetching the user, storing the user data, handling loading states, or refreshing user data. Stack manages all of that for you.
+
+You can also add a button to change the user's name:
+
+```tsx
+
+```
+The user data will be updated in both the frontend and backend automatically. The updated user data will also reflect in `MyComponent` as well.
+
+You also get pages and components for authentication flow out-of-the-box. This for example the sign-in page is what you get without writing a single line of code:
+
+
+
+If you prefer a fully customized UI, you can use our low-level functions like `signInWithOAuth` or `signInWithCredential` to build your own sign-in page:
+
+```tsx
+export default function CustomOAuthSignIn() {
+ const app = useStackApp();
+ return
+
+
;
+}
+```
+
+To manage everything efficiently, there is a powerful admin dashboard:
+
+
+
+Best of all, Stack is **100% open-source**.
+
+This is just a glimpse of what Stack can do. Stack also handles many other tasks like backend integration, data storage, emails, teams, permissions, and more, which you will learn later in the documentation.
+
+If this sounds interesting, let's get started with setting up Stack in your project!
\ No newline at end of file
diff --git a/docs/docs/01-getting-started/01-setup.md b/docs/docs/01-getting-started/02-setup.md
similarity index 79%
rename from docs/docs/01-getting-started/01-setup.md
rename to docs/docs/01-getting-started/02-setup.md
index c3226adec2..fdd74df264 100644
--- a/docs/docs/01-getting-started/01-setup.md
+++ b/docs/docs/01-getting-started/02-setup.md
@@ -1,9 +1,12 @@
---
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
# Installation & Setup
-## Setup wizard (recommended)
+## Setup
To get started with Stack, you need to create a [Next.js](https://nextjs.org/docs) project with the App router. If you are starting from scratch, run the following:
@@ -12,7 +15,13 @@ npx create-next-app@latest --app stack-example
cd stack-example
```
-To add Stack to a newly created or an existing project, you can run Stack's installation wizard with the following command:
+You can choose between two ways to install Stack: the setup wizard or manual installation. We recommend using the setup wizard first as it is very easy. However, if you have a non-standard project structure or the setup wizard doesn't work for you, you can follow the manual installation guide.
+
+
+
+
+
+To setup stack, you can run Stack's installation wizard with the following command:
```sh
npx @stackframe/init-stack@latest
@@ -26,11 +35,9 @@ NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY=
STACK_SECRET_SERVER_KEY=
```
-After that, you'll be able to see the Stack sign-up page at [https://your-website.example.com/handler/signup](http://localhost:3000/handler/signup).
-
-## Manual installation
+
-If the setup wizard doesn't work due to an unsupported project structure, you can also install Stack manually.
+
First, install Stack with npm, yarn, or pnpm:
@@ -59,7 +66,7 @@ npm install @stackframe/stack
This will read the environment variables automatically and create a server app that you can later use to access Stack from your Next.js server.
- Check out the [`StackServerApp` documentation](/docs/api-documentation/app) to learn more about its other options.
+ Check out the [`StackServerApp` documentation](../03-api-documentation/03-app.md) to learn more about its other options.
3. Create a new file in `app/handler/[...stack]/page.tsx` and paste the following code:
@@ -107,8 +114,10 @@ npm install @stackframe/stack
>;
}
```
+
+
-6. That's it! Stack is now configured in your Next.js project. If you start your Next.js app with `npm run dev` and navigate to [http://localhost:3000/handler/signup](http://localhost:3000/handler/signup), you will see the Stack sign-up page!
+That's it! Stack is now configured in your Next.js project. If you start your Next.js app with `npm run dev` and navigate to [http://localhost:3000/handler/signup](http://localhost:3000/handler/signup), you will see the Stack sign-up page!

@@ -117,6 +126,7 @@ npm install @stackframe/stack

+
## Next steps
-Next, we will show you how to get user information, protect a page, and modify the user profile.
+Next, we will show you how to get user information, protect a page, and store/retrieve user information in code.
diff --git a/docs/docs/01-getting-started/02-users.md b/docs/docs/01-getting-started/03-users.md
similarity index 73%
rename from docs/docs/01-getting-started/02-users.md
rename to docs/docs/01-getting-started/03-users.md
index 007e5c70fd..58bcdc51e9 100644
--- a/docs/docs/01-getting-started/02-users.md
+++ b/docs/docs/01-getting-started/03-users.md
@@ -7,7 +7,7 @@ import TabItem from '@theme/TabItem';
# Users & Protected Pages
-In [the last guide](/docs/getting-started/setup), we created `StackServerApp` and `StackProvider`. In this section, we will show you how to utilize them for accessing and modifying the current user information on Server Components and Client Components, respectively.
+In [the last guide](./02-setup.md), we created `StackServerApp` and `StackProvider`. In this section, we will show you how to utilize them for accessing and modifying the current user information on Server Components and Client Components, respectively.
## Client Components
@@ -19,7 +19,7 @@ import { useStackApp } from "@stackframe/stack";
export function MyComponent() {
const app = useStackApp();
- const user = app.useUser();
+ const user = app.useUser(); // or just import useUser and use it directly
return
{user ? `Hello, ${user.displayName ?? "anon"}` : 'You are not logged in'}
;
}
@@ -128,13 +128,11 @@ Stack automatically creates a user profile on sign-up. Let's create a page that
const app = useStackApp();
return (
-
Home
{user ? (
Welcome, {user.displayName}
Your e-mail: {user.primaryEmail}
-
Your e-mail verification status: {user.primaryEmailVerified.toString()}
) : (
@@ -159,13 +157,11 @@ Stack automatically creates a user profile on sign-up. Let's create a page that
const user = await stackApp.getUser();
return (
-
Home
{user ? (
Welcome, {user.displayName}
Your e-mail: {user.primaryEmail}
-
Your e-mail verification status: {user.primaryEmailVerified.toString()}
) : (
@@ -187,3 +183,61 @@ Note the `UserButton` is a component that you normally put in the top right corn

You will now be able to see the new profile page on [http://localhost:3000/profile](http://localhost:3000/profile).
+
+To see more examples of how to use the `User` object, check out the [User API documentation](../03-api-documentation/01-user.md).
+
+## Custom User Information
+
+You can update the user's information by calling `user.update()` with the new data. The user data from the `userUser()` hook is automatically will also be updated automatically. Here is an example:
+
+```tsx
+'user client';
+import { useUser } from "@stackframe/stack";
+
+export default function UpdateUser() {
+ const user = useUser();
+ return ;
+}
+```
+
+You can store custom data in the `clientMetadata` field. Note that this data is visible on the client side and should not contain any sensitive information.
+
+```tsx
+await user.update({
+ clientMetadata: {
+ mailingAddress: "123 Main St",
+ },
+});
+```
+
+You can then get the `clientMetadata` field from the `User` object:
+
+```tsx
+const user = useUser();
+console.log(user.clientMetadata);
+```
+
+If you want to store sensitive information that is only visible on the server side, you can use the `serverMetadata` field. This data will not be sent accessible on the client side. So you also have to use the `getUser()` on the `StackServerApp` to access this data.
+
+```tsx
+// server side only
+import { stackApp } from "@/lib/stack";
+const user = await stackApp.getUser();
+await user.update({
+ serverMetadata: {
+ secretInfo: "This is a secret",
+ },
+});
+```
+
+You can also access them from the `User` object:
+
+```tsx
+// server side only
+import { stackApp } from "@/lib/stack";
+const user = await stackApp.getUser();
+console.log(user.serverMetadata);
+```
+
diff --git a/docs/docs/01-getting-started/03-teams.md b/docs/docs/01-getting-started/04-teams.md
similarity index 100%
rename from docs/docs/01-getting-started/03-teams.md
rename to docs/docs/01-getting-started/04-teams.md
diff --git a/docs/docs/02-customization/01-overview.md b/docs/docs/02-customization/01-overview.md
index d6f56d8983..d981baf96f 100644
--- a/docs/docs/02-customization/01-overview.md
+++ b/docs/docs/02-customization/01-overview.md
@@ -9,11 +9,11 @@ title: Overview
Stack offers comprehensive customization options for its user interface, available across three levels:
-- [**Custom Color Scheme**](/docs/customization/custom-colors): For those looking to quickly align the interface with their brand while minimizing customization efforts, changing the color scheme is an efficient option.
+- [**Custom Color Scheme**](./02-custom-colors.md): For those looking to quickly align the interface with their brand while minimizing customization efforts, changing the color scheme is an efficient option.
-- [**Custom Component**](/docs/customization/custom-components): Stack features a uniquely designed UI system that enables the replacement of any low-level components with alternatives of your choosing. This includes integration with various UI libraries such as MUI, Chakra, or any custom stayled components. You can opt for quick modifications by replacing prominent components like buttons and input fields, or undertake comprehensive UI customization by replacing all components.
+- [**Custom Component**](./03-custom-components.md): Stack features a uniquely designed UI system that enables the replacement of any low-level components with alternatives of your choosing. This includes integration with various UI libraries such as MUI, Chakra, or any custom stayled components. You can opt for quick modifications by replacing prominent components like buttons and input fields, or undertake comprehensive UI customization by replacing all components.
-- [**Custom Pages**](/docs/customization/custom-pages): For complete control over the UI, including alterations to the overall layout or the logic flow of pages, you can design your own pages. This can be done using Stack components or those from your preferred library, supported by our low-level functions to facilitate logic operations and server communication.
+- [**Custom Pages**](./04-custom-pages.md): For complete control over the UI, including alterations to the overall layout or the logic flow of pages, you can design your own pages. This can be done using Stack components or those from your preferred library, supported by our low-level functions to facilitate logic operations and server communication.
These customization levels operate independently, allowing any combination of adjustments. For instance, you might customize the buttons and input fields while retaining the default design for other components, and adapt a custom color scheme as a fallback for the look of the other components.
diff --git a/docs/docs/02-customization/03-custom-components.md b/docs/docs/02-customization/03-custom-components.md
index 91be8f044c..02f2f8eb02 100644
--- a/docs/docs/02-customization/03-custom-components.md
+++ b/docs/docs/02-customization/03-custom-components.md
@@ -12,7 +12,7 @@ We currently already implemented support for MUI Joy, so you can use it directly
## Customizing Components
-Here is an example of how you can customize the button component. For demonstration purposes, we will keep the styling minimal. We will create a new button component. You can use the `useDesign` hook to get the color scheme (see more in [Custom Colors](/docs/customization/custom-colors)). You can also ignore the color scheme if you have a different way to handle colors.
+Here is an example of how you can customize the button component. For demonstration purposes, we will keep the styling minimal. We will create a new button component. You can use the `useDesign` hook to get the color scheme (see more in [Custom Colors](./02-custom-colors.md)). You can also ignore the color scheme if you have a different way to handle colors.
```jsx
'use client';
diff --git a/docs/docs/02-customization/04-custom-pages.md b/docs/docs/02-customization/04-custom-pages.md
index a8445870c2..1ef447ad57 100644
--- a/docs/docs/02-customization/04-custom-pages.md
+++ b/docs/docs/02-customization/04-custom-pages.md
@@ -4,7 +4,7 @@ sidebar_position: 1
# Custom Pages
-By default, `StackHandler` creates all pages you need. You can customize the [colors](/docs/customization/custom-colors) and [components](/docs/customization/custom-components) here. However, if you'd like full control of the layout and logic flow, you can always use our built-in components to build your own pages or use even star from strach using low-level functions.
+By default, `StackHandler` creates all pages you need. You can customize the [colors](./02-custom-colors.md) and [components](./03-custom-components.md) here. However, if you'd like full control of the layout and logic flow, you can always use our built-in components to build your own pages or use even star from strach using low-level functions.
## Simple Example
@@ -78,4 +78,4 @@ As above, visit the `/signin` page to see your newly created custom OAuth page.
## Next steps
-Take a look at the [customization examples](/docs/category/page-examples) to see how to build custom pages for sign in, sign up, reset password, and more.
+Take a look at the [customization examples](./05-page-examples/_category_.json) to see how to build custom pages for sign in, sign up, reset password, and more.
diff --git a/docs/docs/02-customization/05-page-examples/01-signin.md b/docs/docs/02-customization/05-page-examples/01-signin.md
index bf33a248c4..85f6d0beb3 100644
--- a/docs/docs/02-customization/05-page-examples/01-signin.md
+++ b/docs/docs/02-customization/05-page-examples/01-signin.md
@@ -8,11 +8,12 @@ sidebar_position: 1
```tsx
'use client';
-import { useStackApp, SignIn } from "@stackframe/stack";
+import { SignIn } from "@stackframe/stack";
export default function DefaultSignIn() {
- const app = useStackApp();
-
+ // optionally redirect to some other page if the user is already signed in
+ // const user = useUser();
+ // if (user) { redirect to some other page }
return ;
}
```
@@ -24,8 +25,6 @@ You can also use `useUser` at the beginning of the sign in page to check if weth
`CredentialSignIn`: A component that contains a form for signing in with email and password.
-`PasswordField`: password input field with show/hide password button.
-
`OAuthGroup`: A list of available OAuth provider signin buttons components. The available provider list is fetched from the server.
`OAuthButton`: A single OAuth sign in button.
@@ -70,21 +69,60 @@ export default function CustomCredentialSignIn() {
setError('Please enter your password');
return;
}
- // this will rediret to app.urls.afterSignIn if successful, you can customize it in the StackServerApp constructor
+ // this will redirect to app.urls.afterSignIn if successful, you can customize it in the StackServerApp constructor
const errorCode = await app.signInWithCredential({ email, password });
// It is better to handle each error code separately, but we will just show the error code directly for simplicity here
if (errorCode) {
- setError(errorCode);
+ setError(errorCode.message);
}
};
return (
-
+
+
+
);
}
```
+
+## Custom Magic Link Sign In
+
+```tsx
+'use client';
+
+import { useStackApp } from "@stackframe/stack";
+import { useState } from "react";
+
+export default function CustomCredentialSignIn() {
+ const [email, setEmail] = useState('');
+ const [error, setError] = useState('');
+ const [message, setMessage] = useState('');
+ const app = useStackApp();
+
+ const onSubmit = async () => {
+ // this will redirect to app.urls.afterSignIn if successful, you can customize it in the StackServerApp constructor
+ const errorCode = await app.sendMagicLinkEmail(email);
+ // It is better to handle each error code separately, but we will just show the error code directly for simplicity here
+ if (errorCode) {
+ setError(errorCode.message);
+ } else {
+ setMessage('Magic link sent! Please check your email.');
+ }
+ };
+
+ return (
+
+ );
+}
+```
\ No newline at end of file
diff --git a/docs/docs/02-customization/05-page-examples/02-signup.md b/docs/docs/02-customization/05-page-examples/02-signup.md
index 450faac42f..8bda43f12e 100644
--- a/docs/docs/02-customization/05-page-examples/02-signup.md
+++ b/docs/docs/02-customization/05-page-examples/02-signup.md
@@ -8,12 +8,13 @@ sidebar_position: 1
```tsx
'use client';
-import { useStackApp, SignUp } from "@stackframe/stack";
+import { SignUp } from "@stackframe/stack";
export default function DefaultSignUp() {
- const app = useStackApp();
-
- return ;
+ // optionally redirect to some other page if the user is already signed in
+ // const user = useUser();
+ // if (user) { redirect to some other page }
+ return ;
}
```
@@ -23,29 +24,52 @@ You can also use `useUser` at the beginning of the sign in page to check if weth
`CredentialSignUp`: A component that contains a form for signing in with email and password.
-`PasswordField`: password input field with show/hide password button.
-
`OAuthGroup`: A list of available OAuth provider sign-up buttons components. The available provider list is fetched from the server.
`OAuthButton`: A single OAuth sign-up button.
## Custom OAuth Sign Up
+OAuth sign-in and sign-up shares the same function. Check out the [Sign In example](/docs/customization/page-examples/signin#custom-oauth-sign-in) for more information.
+
+## Custom Credential Sign Up
+
```tsx
'use client';
+
import { useStackApp } from "@stackframe/stack";
+import { useState } from "react";
-export default function CustomOAuthSignUp() {
+export default function CustomCredentialSignUp() {
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+ const [error, setError] = useState('');
const app = useStackApp();
- return