Skip to content

Add to Existing Project

Supabase Modules have been designed with Next in mind. So after setting up your Next application or in your existing Next application, here is what you will need:

Before proceeding

These instructions are based on a Next 14 application. More frameworks coming soon.

1. Installing dependencies

bash
pnpm add @tanstack/react-query @supabase/ssr @supabase/supabase-js

Supabase CLI

To run the entire Supabase Stack locally on your machine, you will have to decide whether you want to add the Supabase CLI as a dev dependency, execute it using your package manager or install it globally using Homebrew

bash
pnpm add -D supabase
bash
pnpm dlx supabase <command>
bash
brew install supabase/tap/supabase

You will also need to update your package.json scripts to use Supabase CLI:

json
"scripts": {
  "db:init": "supabase init", 
  "db:start": "supabase start", 
  "db:stop": "supabase stop", 
  "db:reset": "supabase db reset", 
  "db:gen-types": "supabase gen types typescript --local > modules/types/index.ts"
}

2. Initialize or scaffold your Supabase project

You can chose whether you want to start with a new Supabase project or if you want to scaffold your Supabase project files:

bash
pnpm db:init
bash
supabase init
bash
degit webscopeio/supabase-modules/apps/next/supabase supabase

From either of these you should have a /supabase directory in your project with at least config.toml and seed.sql files.

INFO

When you initialize a Supabase projecet. You do not need to generate VS Code settings for Deno.

3. Set environment variables

Update or create an .env.local file to store your environment variables.

example
# Since .env is gitignored, you can use .env.example to build a new `.env` file when you clone the repo.
# Keep this file up-to-date when you add new variables to \`.env\`.
# These examples are for development use only

NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0

The .env.example file above contains the environment variables that you can use for local development.

To generate these environment variables you have to run Supabase locally first before development. Read more on Your Supabase Instance.

4. Add Query and API Route Handler to your project

TanStack Query

To use TanStack Query you need to set a QueryClientProvider. Typically you would add this to your list of providers or create a provider if you don't have one already.

tsx
"use client"

import * as React from "react"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { ThemeProvider as NextThemesProvider } from "next-themes"

import { Toaster } from "@/components/ui/sonner"
import { TooltipProvider } from "@/components/ui/tooltip"

export const Providers: React.FC<React.PropsWithChildren> = ({ children }) => {
  const [queryClient] = React.useState(
    () =>
      new QueryClient({
        defaultOptions: {
          queries: {
            staleTime: 60 * 1000,
          },
        },
      })
  )
  return (
    <QueryClientProvider client={queryClient}>
      <NextThemesProvider
        attribute="class"
        defaultTheme="system"
        enableSystem
        disableTransitionOnChange
      >
        <TooltipProvider>{children}</TooltipProvider>
        <Toaster />
      </NextThemesProvider>
    </QueryClientProvider>
  )
}

This provider has to be included in the project code. In a Next 13 application this would be in the mainlayout.tsx file. For more, see TanStack Query Next.js app with streaming.

API Route Handler

Chances are that you will need this route handler.

ts
import { NextResponse, type NextRequest } from "next/server"
import { type EmailOtpType } from "@supabase/supabase-js"

import { createClient } from "@/modules/utils/server"

export async function GET(request: NextRequest) {
  const { searchParams } = new URL(request.url)
  const type = searchParams.get("type") as EmailOtpType | null
  const token_hash = searchParams.get("token_hash")
  const next = searchParams.get("next") ?? "/"

  const redirectTo = request.nextUrl.clone()
  redirectTo.pathname = next
  redirectTo.searchParams.delete("next")
  redirectTo.searchParams.delete("token_hash")
  redirectTo.searchParams.delete("type")

  if (token_hash && type) {
    const supabase = createClient()

    const { error } = await supabase.auth.verifyOtp({
      type,
      token_hash,
    })

    if (error) {
      redirectTo.pathname = "/login"
      redirectTo.searchParams.set(
        "error",
        JSON.stringify({ message: error.message, status: error.status || 401 })
      )
    }

    return NextResponse.redirect(redirectTo)
  }
}

5. Start your development server

Before proceeding

To work with a local Supabase Instance you need to run a database container with a running docker daemon. We recommend getting Docker. You can find instructions on Get Docker

bash
pnpm db:start && pnpm dev
bash
pnpm dev

🎉 Congratulations!

You are done! See Installation to install individual Modules. Remember to run pnpm db:stop to save resources once you are done working with your local Supabase Instance.

Recommendations

It is recommended to also use TanStack's ESLint Plugin Query to help you catch bugs and inconsistencies while you code.

bash
pnpm add -D @tanstack/eslint-plugin-query

Make sure to read their Usage Instructions for setup.

ESLint Configuration Example

Here is an example of a simple setup for a Next TypeScript React project:

  1. First install the dev dependencies (here we're included all of the dependencies for the config):
bash
pnpm add -D eslint eslint-config-next @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-prettier eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-tailwindcss
  1. Then configure your eslintc.json
json
{
  "extends": [
    "next/core-web-vitals",
    "eslint:recommended",
    "prettier",
    "plugin:@typescript-eslint/recommended",
    "plugin:@typescript-eslint/recommended-requiring-type-checking",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended",
    "plugin:@tanstack/eslint-plugin-query/recommended",
    "plugin:tailwindcss/recommended"
  ],
  "plugins": ["@typescript-eslint", "react", "react-hooks", "tailwindcss"],
  "root": true,
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "project": true,
    "ecmaFeatures": {
      "jsx": true
    }
  },
  "rules": {
    "@typescript-eslint/consistent-type-definitions": "off",
    "@typescript-eslint/no-misused-promises": [
      "error",
      {
        "checksVoidReturn": {
          "attributes": false
        }
      }
    ],
    "no-console": ["error", { "allow": ["error"] }],
    "react/prop-types": "off",
    "react/react-in-jsx-scope": "off",
    "tailwindcss/no-custom-classname": "off",
    "tailwindcss/classnames-order": "off"
  },
  "settings": {
    "tailwindcss": {
      "callees": ["cn", "cva"],
      "config": "tailwind.config.ts"
    },
    "react": {
      "version": "detect"
    }
  },
  "ignorePatterns": ["/lib/env.ts", "/postcss.config.js", "/modules/types"]
}