Skip to content

Commit e42a4a0

Browse files
committed
Initial commit from Create Next App
0 parents  commit e42a4a0

33 files changed

+3214
-0
lines changed

.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Update these with your Supabase details from your project settings > API
2+
# https://app.supabase.com/project/_/settings/api
3+
NEXT_PUBLIC_SUPABASE_URL=your-project-url
4+
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key

.gitignore

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
.yarn/install-state.gz
8+
9+
# testing
10+
/coverage
11+
12+
# next.js
13+
/.next/
14+
/out/
15+
16+
# production
17+
/build
18+
19+
# misc
20+
.DS_Store
21+
*.pem
22+
23+
# debug
24+
npm-debug.log*
25+
yarn-debug.log*
26+
yarn-error.log*
27+
28+
# local env files
29+
.env*.local
30+
31+
# vercel
32+
.vercel
33+
34+
# typescript
35+
*.tsbuildinfo
36+
next-env.d.ts

README.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<a href="https://demo-nextjs-with-supabase.vercel.app/">
2+
<img alt="Next.js and Supabase Starter Kit - the fastest way to build apps with Next.js and Supabase" src="https://demo-nextjs-with-supabase.vercel.app/opengraph-image.png">
3+
<h1 align="center">Next.js and Supabase Starter Kit</h1>
4+
</a>
5+
6+
<p align="center">
7+
The fastest way to build apps with Next.js and Supabase
8+
</p>
9+
10+
<p align="center">
11+
<a href="#features"><strong>Features</strong></a> ·
12+
<a href="#demo"><strong>Demo</strong></a> ·
13+
<a href="#deploy-to-vercel"><strong>Deploy to Vercel</strong></a> ·
14+
<a href="#clone-and-run-locally"><strong>Clone and run locally</strong></a> ·
15+
<a href="#feedback-and-issues"><strong>Feedback and issues</strong></a>
16+
<a href="#more-supabase-examples"><strong>More Examples</strong></a>
17+
</p>
18+
<br/>
19+
20+
## Features
21+
22+
- Works across the entire [Next.js](https://nextjs.org) stack
23+
- App Router
24+
- Pages Router
25+
- Middleware
26+
- Client
27+
- Server
28+
- It just works!
29+
- supabase-ssr. A package to configure Supabase Auth to use cookies
30+
- Styling with [Tailwind CSS](https://tailwindcss.com)
31+
- Optional deployment with [Supabase Vercel Integration and Vercel deploy](#deploy-your-own)
32+
- Environment variables automatically assigned to Vercel project
33+
34+
## Demo
35+
36+
You can view a fully working demo at [demo-nextjs-with-supabase.vercel.app](https://demo-nextjs-with-supabase.vercel.app/).
37+
38+
## Deploy to Vercel
39+
40+
Vercel deployment will guide you through creating a Supabase account and project.
41+
42+
After installation of the Supabase integration, all relevant environment variables will be assigned to the project so the deployment is fully functioning.
43+
44+
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fnext.js%2Ftree%2Fcanary%2Fexamples%2Fwith-supabase&project-name=nextjs-with-supabase&repository-name=nextjs-with-supabase&demo-title=nextjs-with-supabase&demo-description=This%20starter%20configures%20Supabase%20Auth%20to%20use%20cookies%2C%20making%20the%20user's%20session%20available%20throughout%20the%20entire%20Next.js%20app%20-%20Client%20Components%2C%20Server%20Components%2C%20Route%20Handlers%2C%20Server%20Actions%20and%20Middleware.&demo-url=https%3A%2F%2Fdemo-nextjs-with-supabase.vercel.app%2F&external-id=https%3A%2F%2Fgithub.com%2Fvercel%2Fnext.js%2Ftree%2Fcanary%2Fexamples%2Fwith-supabase&demo-image=https%3A%2F%2Fdemo-nextjs-with-supabase.vercel.app%2Fopengraph-image.png&integration-ids=oac_VqOgBHqhEoFTPzGkPd7L0iH6)
45+
46+
The above will also clone the Starter kit to your GitHub, you can clone that locally and develop locally.
47+
48+
If you wish to just develop locally and not deploy to Vercel, [follow the steps below](#clone-and-run-locally).
49+
50+
## Clone and run locally
51+
52+
1. You'll first need a Supabase project which can be made [via the Supabase dashboard](https://database.new)
53+
54+
2. Create a Next.js app using the Supabase Starter template npx command
55+
56+
```bash
57+
npx create-next-app -e with-supabase
58+
```
59+
60+
3. Use `cd` to change into the app's directory
61+
62+
```bash
63+
cd name-of-new-app
64+
```
65+
66+
4. Rename `.env.local.example` to `.env.local` and update the following:
67+
68+
```
69+
NEXT_PUBLIC_SUPABASE_URL=[INSERT SUPABASE PROJECT URL]
70+
NEXT_PUBLIC_SUPABASE_ANON_KEY=[INSERT SUPABASE PROJECT API ANON KEY]
71+
```
72+
73+
Both `NEXT_PUBLIC_SUPABASE_URL` and `NEXT_PUBLIC_SUPABASE_ANON_KEY` can be found in [your Supabase project's API settings](https://app.supabase.com/project/_/settings/api)
74+
75+
5. You can now run the Next.js local development server:
76+
77+
```bash
78+
npm run dev
79+
```
80+
81+
The starter kit should now be running on [localhost:3000](http://localhost:3000/).
82+
83+
> Check out [the docs for Local Development](https://supabase.com/docs/guides/getting-started/local-development) to also run Supabase locally.
84+
85+
## Feedback and issues
86+
87+
Please file feedback and issues over on the [Supabase GitHub org](https://github.com/supabase/supabase/issues/new/choose).
88+
89+
## More Supabase examples
90+
91+
- [Next.js Subscription Payments Starter](https://github.com/vercel/nextjs-subscription-payments)
92+
- [Cookie-based Auth and the Next.js 13 App Router (free course)](https://youtube.com/playlist?list=PL5S4mPUpp4OtMhpnp93EFSo42iQ40XjbF)
93+
- [Supabase Auth and the Next.js App Router](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs)

app/auth/callback/route.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { createClient } from "@/utils/supabase/server";
2+
import { NextResponse } from "next/server";
3+
4+
export async function GET(request: Request) {
5+
// The `/auth/callback` route is required for the server-side auth flow implemented
6+
// by the SSR package. It exchanges an auth code for the user's session.
7+
// https://supabase.com/docs/guides/auth/server-side/nextjs
8+
const requestUrl = new URL(request.url);
9+
const code = requestUrl.searchParams.get("code");
10+
const origin = requestUrl.origin;
11+
12+
if (code) {
13+
const supabase = createClient();
14+
await supabase.auth.exchangeCodeForSession(code);
15+
}
16+
17+
// URL to redirect to after sign up process completes
18+
return NextResponse.redirect(`${origin}/protected`);
19+
}

app/favicon.ico

25.3 KB
Binary file not shown.

app/globals.css

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
@tailwind base;
2+
@tailwind components;
3+
@tailwind utilities;
4+
5+
@layer base {
6+
:root {
7+
--background: 200 20% 98%;
8+
--btn-background: 200 10% 91%;
9+
--btn-background-hover: 200 10% 89%;
10+
--foreground: 200 50% 3%;
11+
}
12+
13+
@media (prefers-color-scheme: dark) {
14+
:root {
15+
--background: 200 50% 3%;
16+
--btn-background: 200 10% 9%;
17+
--btn-background-hover: 200 10% 12%;
18+
--foreground: 200 20% 96%;
19+
}
20+
}
21+
}
22+
23+
@layer base {
24+
* {
25+
@apply border-foreground/20;
26+
}
27+
}
28+
29+
.animate-in {
30+
animation: animateIn 0.3s ease 0.15s both;
31+
}
32+
33+
@keyframes animateIn {
34+
from {
35+
opacity: 0;
36+
transform: translateY(10px);
37+
}
38+
to {
39+
opacity: 1;
40+
transform: translateY(0);
41+
}
42+
}

app/layout.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { GeistSans } from "geist/font/sans";
2+
import "./globals.css";
3+
4+
const defaultUrl = process.env.VERCEL_URL
5+
? `https://${process.env.VERCEL_URL}`
6+
: "http://localhost:3000";
7+
8+
export const metadata = {
9+
metadataBase: new URL(defaultUrl),
10+
title: "Next.js and Supabase Starter Kit",
11+
description: "The fastest way to build apps with Next.js and Supabase",
12+
};
13+
14+
export default function RootLayout({
15+
children,
16+
}: {
17+
children: React.ReactNode;
18+
}) {
19+
return (
20+
<html lang="en" className={GeistSans.className}>
21+
<body className="bg-background text-foreground">
22+
<main className="min-h-screen flex flex-col items-center">
23+
{children}
24+
</main>
25+
</body>
26+
</html>
27+
);
28+
}

app/login/page.tsx

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import Link from "next/link";
2+
import { headers } from "next/headers";
3+
import { createClient } from "@/utils/supabase/server";
4+
import { redirect } from "next/navigation";
5+
import { SubmitButton } from "./submit-button";
6+
7+
export default function Login({
8+
searchParams,
9+
}: {
10+
searchParams: { message: string };
11+
}) {
12+
const signIn = async (formData: FormData) => {
13+
"use server";
14+
15+
const email = formData.get("email") as string;
16+
const password = formData.get("password") as string;
17+
const supabase = createClient();
18+
19+
const { error } = await supabase.auth.signInWithPassword({
20+
email,
21+
password,
22+
});
23+
24+
if (error) {
25+
return redirect("/login?message=Could not authenticate user");
26+
}
27+
28+
return redirect("/protected");
29+
};
30+
31+
const signUp = async (formData: FormData) => {
32+
"use server";
33+
34+
const origin = headers().get("origin");
35+
const email = formData.get("email") as string;
36+
const password = formData.get("password") as string;
37+
const supabase = createClient();
38+
39+
const { error } = await supabase.auth.signUp({
40+
email,
41+
password,
42+
options: {
43+
emailRedirectTo: `${origin}/auth/callback`,
44+
},
45+
});
46+
47+
if (error) {
48+
return redirect("/login?message=Could not authenticate user");
49+
}
50+
51+
return redirect("/login?message=Check email to continue sign in process");
52+
};
53+
54+
return (
55+
<div className="flex-1 flex flex-col w-full px-8 sm:max-w-md justify-center gap-2">
56+
<Link
57+
href="/"
58+
className="absolute left-8 top-8 py-2 px-4 rounded-md no-underline text-foreground bg-btn-background hover:bg-btn-background-hover flex items-center group text-sm"
59+
>
60+
<svg
61+
xmlns="http://www.w3.org/2000/svg"
62+
width="24"
63+
height="24"
64+
viewBox="0 0 24 24"
65+
fill="none"
66+
stroke="currentColor"
67+
strokeWidth="2"
68+
strokeLinecap="round"
69+
strokeLinejoin="round"
70+
className="mr-2 h-4 w-4 transition-transform group-hover:-translate-x-1"
71+
>
72+
<polyline points="15 18 9 12 15 6" />
73+
</svg>{" "}
74+
Back
75+
</Link>
76+
77+
<form className="animate-in flex-1 flex flex-col w-full justify-center gap-2 text-foreground">
78+
<label className="text-md" htmlFor="email">
79+
Email
80+
</label>
81+
<input
82+
className="rounded-md px-4 py-2 bg-inherit border mb-6"
83+
name="email"
84+
placeholder="you@example.com"
85+
required
86+
/>
87+
<label className="text-md" htmlFor="password">
88+
Password
89+
</label>
90+
<input
91+
className="rounded-md px-4 py-2 bg-inherit border mb-6"
92+
type="password"
93+
name="password"
94+
placeholder="••••••••"
95+
required
96+
/>
97+
<SubmitButton
98+
formAction={signIn}
99+
className="bg-green-700 rounded-md px-4 py-2 text-foreground mb-2"
100+
pendingText="Signing In..."
101+
>
102+
Sign In
103+
</SubmitButton>
104+
<SubmitButton
105+
formAction={signUp}
106+
className="border border-foreground/20 rounded-md px-4 py-2 text-foreground mb-2"
107+
pendingText="Signing Up..."
108+
>
109+
Sign Up
110+
</SubmitButton>
111+
{searchParams?.message && (
112+
<p className="mt-4 p-4 bg-foreground/10 text-foreground text-center">
113+
{searchParams.message}
114+
</p>
115+
)}
116+
</form>
117+
</div>
118+
);
119+
}

app/login/submit-button.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"use client";
2+
3+
import { useFormStatus } from "react-dom";
4+
import { type ComponentProps } from "react";
5+
6+
type Props = ComponentProps<"button"> & {
7+
pendingText?: string;
8+
};
9+
10+
export function SubmitButton({ children, pendingText, ...props }: Props) {
11+
const { pending, action } = useFormStatus();
12+
13+
const isPending = pending && action === props.formAction;
14+
15+
return (
16+
<button {...props} type="submit" aria-disabled={pending}>
17+
{isPending ? pendingText : children}
18+
</button>
19+
);
20+
}

app/opengraph-image.png

283 KB
Loading

0 commit comments

Comments
 (0)