diff --git a/README.md b/README.md index 537a7ab90..718882e7f 100644 --- a/README.md +++ b/README.md @@ -16,12 +16,11 @@ Here is an example of the sign-up page you get out of the box: ## Features +- Composable React components & hooks - OAuth (Google, Facebook, GitHub, etc.) - Email and password authentication (with email verification and password reset) - Easy to set up with proxied providers - User management & analytics -- Pre-built React components & hooks -- Support for static sites & single-page apps - User-associated metadata with client-/server-specific permissions - **100% open-source!** diff --git a/docs/docs/01-getting-started/01-setup.md b/docs/docs/01-getting-started/01-setup.md index 37f28dfd9..55c027554 100644 --- a/docs/docs/01-getting-started/01-setup.md +++ b/docs/docs/01-getting-started/01-setup.md @@ -10,6 +10,7 @@ To get started with Stack, you need to create a [Next.js](https://nextjs.org/doc ```bash npx create-next-app@latest --app stack-example +cd stack-example ``` Once that's done, you can install Stack with npm or yarn: @@ -39,9 +40,9 @@ npm install @stackframe/stack }); ``` - This will reads the environment variables automatically and create a server app that you can later use to access Stack from your Next.js server. + 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. - `StackServerApp` has many other options. Check out [StackServerApp documentation](/docs/api-documentation/app) if you want to learn more. + Check out the [`StackServerApp` documentation](/docs/api-documentation/app) to learn more about its other options. 3. Create a new file in `app/handler/[...stack]/page.tsx` and paste the following code: @@ -57,14 +58,14 @@ npm install @stackframe/stack This will create pages for sign-in, sign-up, password reset, and others. Additionally, it will be used as a callback URL for OAuth. You can [replace them with your own pages](/docs/advanced-guides/customization/overview) later. -4. In your `app/layout.tsx`, wrap your entire layout with a `StackProvider`. Afterwards, it should look like this: +4. In your `app/layout.tsx`, wrap the entire body with a `StackProvider`. Afterwards, it should look like this: ```tsx import React from "react"; import { StackProvider } from "@stackframe/stack"; import { stackApp } from "@/lib/stack"; - export default function Layout({ children }: { children: React.ReactNode }) { + export default function RootLayout({ children }: { children: React.ReactNode }) { return ( @@ -77,9 +78,6 @@ npm install @stackframe/stack } ``` - This lets you use the `useStackApp()` and `useUser()` hooks. - - 5. By default, Stack uses [`Suspense`](https://react.dev/reference/react/Suspense) to handle loading states. To show a loading indicator while Stack is fetching user data, make sure there is a `loading.tsx` file in your `app` directory: ```tsx diff --git a/docs/docs/01-getting-started/02-users.md b/docs/docs/01-getting-started/02-users.md index a51e06a34..5a320ee1b 100644 --- a/docs/docs/01-getting-started/02-users.md +++ b/docs/docs/01-getting-started/02-users.md @@ -22,7 +22,7 @@ export function MyComponent() { const app = useStackApp(); const user = app.useUser(); - return
{user ? `Hello, ${user.displayName}` : 'You are not logged in'}
; + return
{user ? `Hello, ${user.displayName ?? "anon"}` : 'You are not logged in'}
; } ``` @@ -39,7 +39,7 @@ import { stackApp } from "@/lib/stack"; export default async function MyComponent() { const user = await stackApp.getUser(); - return
{user ? `Hello, ${user.displayName}` : 'You are not logged in'}
; + return
{user ? `Hello, ${user.displayName ?? "anon"}` : 'You are not logged in'}
; } ``` @@ -58,98 +58,128 @@ Call `useUser` (or `getUser`) with the `{ or: 'redirect' }` option to protect th ```tsx "use client"; - import { useStackApp } from "@stackframe/stack"; + import { useUser } from "@stackframe/stack"; export default function Protected() { useUser({ or: 'redirect' }); return

You can only see this if you are logged in

} ``` -
+ - + ```tsx - import { useStackApp } from "@stackframe/stack"; + import { stackApp } from "@/lib/stack"; export default async function Protected() { - await stack.getUser({ or: 'redirect' }); + await stackApp.getUser({ or: 'redirect' }); return

You can only see this if you are logged in

} ```
+ +## Signing out + +You can sign out the user by redirecting them to `/handler/signout` or simply by calling `user.signOut()`. + + + + + ```tsx + "use client"; + import { useUser } from "@stackframe/stack"; + + export default function SignOutButton() { + const user = useUser(); + return ; + } + ``` + + + + ```tsx + import { stackApp } from "@/lib/stack"; + + export default async function SignOutLink() { + return + Sign Out + ; + } + ``` + + + + ## Examples ### User profile -Stack automatically creates a user profile on sign-up. Let's create a page that displays this information. - -If you want to use the client component version, create a new file with the following code and import it to `app/page.tsx`. If you want to use the server component version, paste the code directly into `app/page.tsx`. +Stack automatically creates a user profile on sign-up. Let's create a page that displays this information. In `app/profile/page.tsx`: -```tsx -'use client'; -import { useUser, useStackApp } from "@stackframe/stack"; + ```tsx + 'use client'; + import { useUser, useStackApp } from "@stackframe/stack"; -export default function PageClient() { - const user = useUser(); - const app = useStackApp(); - return ( -
-

Home

- {user ? ( + export default function PageClient() { + const user = useUser(); + const app = useStackApp(); + return (
-

Welcome, {user.displayName}

-

Your e-mail: {user.primaryEmail}

-

Your e-mail verification status: {user.primaryEmailVerified}

- +

Home

+ {user ? ( +
+

Welcome, {user.displayName}

+

Your e-mail: {user.primaryEmail}

+

Your e-mail verification status: {user.primaryEmailVerified.toString()}

+ +
+ ) : ( +
+

You are not logged in

+ + +
+ )}
- ) : ( -
-

You are not logged in

- - -
- )} -
- ); -} -``` + ); + } + ```
-```tsx -import { stackApp } from "@/lib/stack"; + ```tsx + import { stackApp } from "@/lib/stack"; -export default async function Page() { - const user = await stackApp.getUser(); - return ( -
-

Home

- {user ? ( + export default async function Page() { + const user = await stackApp.getUser(); + return (
-

Welcome, {user.displayName}

-

Your e-mail: {user.primaryEmail}

-

Sign Out

+

Home

+ {user ? ( +
+

Welcome, {user.displayName}

+

Your e-mail: {user.primaryEmail}

+

Your e-mail verification status: {user.primaryEmailVerified.toString()}

+

Sign Out

+
+ ) : ( +
+

You are not logged in

+

Sign in

+

Sign up

+
+ )}
- ) : ( -
-

You are not logged in

-

Sign in

-

Sign up

-
- )} -
- ); -} -``` + ); + } + ```
-Now, if you visit http://localhost:3000, you should see the main page with user information or sign-in/ - -## Next steps - -Next, we will take a look at the actions that can be taken from the server-side. +You will now be able to see the new profile page on http://localhost:3000/profile. diff --git a/packages/stack-server/src/components/env-keys.tsx b/packages/stack-server/src/components/env-keys.tsx index 2397d2b11..665f36bf1 100644 --- a/packages/stack-server/src/components/env-keys.tsx +++ b/packages/stack-server/src/components/env-keys.tsx @@ -23,7 +23,7 @@ export default function EnvKeys(props: { STACK_SECRET_SERVER_KEY: props.secretServerKey, STACK_SUPER_SECRET_ADMIN_KEY: props.superSecretAdminKey, }).filter(([k, v]) => v).map(([k, v]) => `${k}=${v}`).join("\n")} - label="Environment variables" + label="Next.js Environment variables" helper={<>Copy these variables into your .env.local file.} /> diff --git a/packages/stack/package.json b/packages/stack/package.json index 8bfddbecc..a85d0d6b5 100644 --- a/packages/stack/package.json +++ b/packages/stack/package.json @@ -1,8 +1,6 @@ { "name": "@stackframe/stack", "version": "1.2.1", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", "type": "module", "scripts": { "typecheck": "tsc --noEmit", @@ -16,6 +14,12 @@ "files": [ "dist" ], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, "dependencies": { "js-cookie": "^3.0.5", "oauth4webapi": "^2.10.3", @@ -26,7 +30,7 @@ "server-only": "^0.0.1" }, "peerDependencies": { - "next": "^14", + "next": "^14.1", "react": "^18.2.0" }, "devDependencies": {