Minor clarity updates

This commit is contained in:
Stan Wohlwend 2024-03-08 05:38:27 +01:00
parent efe79f263e
commit e1b188fede
5 changed files with 106 additions and 75 deletions

View File

@ -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!**

View File

@ -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 (
<html lang="en">
<body>
@ -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

View File

@ -22,7 +22,7 @@ export function MyComponent() {
const app = useStackApp();
const user = app.useUser();
return <div>{user ? `Hello, ${user.displayName}` : 'You are not logged in'}</div>;
return <div>{user ? `Hello, ${user.displayName ?? "anon"}` : 'You are not logged in'}</div>;
}
```
@ -39,7 +39,7 @@ import { stackApp } from "@/lib/stack";
export default async function MyComponent() {
const user = await stackApp.getUser();
return <div>{user ? `Hello, ${user.displayName}` : 'You are not logged in'}</div>;
return <div>{user ? `Hello, ${user.displayName ?? "anon"}` : 'You are not logged in'}</div>;
}
```
@ -58,98 +58,128 @@ Call `useUser` (or `getUser`) with the `{ or: 'redirect' }` option to protect th
<TabItem value="client" label="Client Component" default>
```tsx
"use client";
import { useStackApp } from "@stackframe/stack";
import { useUser } from "@stackframe/stack";
export default function Protected() {
useUser({ or: 'redirect' });
return <h1>You can only see this if you are logged in</h1>
}
```
</TabItem>
</TabItem>
<TabItem value="server" label="Server Component">
<TabItem value="server" label="Server Component">
```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 <h1>You can only see this if you are logged in</h1>
}
```
</TabItem>
</Tabs>
## Signing out
You can sign out the user by redirecting them to `/handler/signout` or simply by calling `user.signOut()`.
<Tabs>
<TabItem value="client" label="user.signOut()" default>
```tsx
"use client";
import { useUser } from "@stackframe/stack";
export default function SignOutButton() {
const user = useUser();
return <button onClick={() => user?.signOut()}>
Sign Out
</button>;
}
```
</TabItem>
<TabItem value="server" label="Redirect">
```tsx
import { stackApp } from "@/lib/stack";
export default async function SignOutLink() {
return <a href={stackApp.urls.signOut}>
Sign Out
</a>;
}
```
</TabItem>
</Tabs>
## 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`:
<Tabs>
<TabItem value="client" label="Client Component" default>
```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 (
<div>
<h1>Home</h1>
{user ? (
export default function PageClient() {
const user = useUser();
const app = useStackApp();
return (
<div>
<p>Welcome, {user.displayName}</p>
<p>Your e-mail: {user.primaryEmail}</p>
<p>Your e-mail verification status: {user.primaryEmailVerified}</p>
<button onClick={() => user.signOut()}>Sign Out</button>
<h1>Home</h1>
{user ? (
<div>
<p>Welcome, {user.displayName}</p>
<p>Your e-mail: {user.primaryEmail}</p>
<p>Your e-mail verification status: {user.primaryEmailVerified.toString()}</p>
<button onClick={() => user.signOut()}>Sign Out</button>
</div>
) : (
<div>
<p>You are not logged in</p>
<button onClick={() => app.redirectToSignIn()}>Sign in</button>
<button onClick={() => app.redirectToSignUp()}>Sign up</button>
</div>
)}
</div>
) : (
<div>
<p>You are not logged in</p>
<button onClick={() => app.redirectToSignIn()}>Sign in</button>
<button onClick={() => app.redirectToSignUp()}>Sign up</button>
</div>
)}
</div>
);
}
```
);
}
```
</TabItem>
<TabItem value="server" label="Server Component">
```tsx
import { stackApp } from "@/lib/stack";
```tsx
import { stackApp } from "@/lib/stack";
export default async function Page() {
const user = await stackApp.getUser();
return (
<div>
<h1>Home</h1>
{user ? (
export default async function Page() {
const user = await stackApp.getUser();
return (
<div>
<p>Welcome, {user.displayName}</p>
<p>Your e-mail: {user.primaryEmail}</p>
<p><a href={stackApp.urls.signOut}>Sign Out</a></p>
<h1>Home</h1>
{user ? (
<div>
<p>Welcome, {user.displayName}</p>
<p>Your e-mail: {user.primaryEmail}</p>
<p>Your e-mail verification status: {user.primaryEmailVerified.toString()}</p>
<p><a href={stackApp.urls.signOut}>Sign Out</a></p>
</div>
) : (
<div>
<p>You are not logged in</p>
<p><a href={stackApp.urls.signIn}>Sign in</a></p>
<p><a href={stackApp.urls.signUp}>Sign up</a></p>
</div>
)}
</div>
) : (
<div>
<p>You are not logged in</p>
<p><a href={stackApp.urls.signIn}>Sign in</a></p>
<p><a href={stackApp.urls.signUp}>Sign up</a></p>
</div>
)}
</div>
);
}
```
);
}
```
</TabItem>
</Tabs>
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.

View File

@ -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 <InlineCode>.env.local</InlineCode> file.</>}
/>
</TabPanel>

View File

@ -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": {