Next.js + Supabase Starter

Next.js + Supabase Starter

Jumpstart your full-stack project with Next.js and Supabase. Authentication, database, and API routes - all set up and ready to go!
best for e-commerce, SaaS, Dashboards.

Key Features

âš¡ Next.js 13 App Router

Leverage the latest Next.js features with the App Router

âš¡ Supabase Authentication

Pre-configured authentication with Supabase

âš¡ Supabase Database

Easy database setup and management with Supabase

âš¡ Tailwind CSS & shadcn/ui

Styled components ready to use.

âš¡ React Hook Form

Efficient and flexible form handling with ZOD validation integrated.

âš¡ TypeScript Support

Type Safe with typescript error handling.

âš¡ React Query Integration

Efficient data fetching, caching, and synchronization.

âš¡ Built-in Hooks

Custom hooks for fetching and mutating data with optimistic updates.

âš¡ Uses pnpm

Using pnpm for faster installs and efficient dependency handling.

Get Started

To get started with this Next.js and Supabase starter, follow these steps to start effortlessly:

  1. Create a new project using the CLI:npx create-next-supabase-starter my-project
  2. Navigate to your project folder:cd my-project
  3. Set up your environment variables: Rename the .env.examplefile to .env.local and update it with your Supabase credentials.NEXT_PUBLIC_SUPABASE_URL ,NEXT_PUBLIC_SUPABASE_ANON_KEY
  4. Install dependencies using pnpm if it's skipped:pnpm install
  5. Run the development server:pnpm dev

Once the setup is complete, open http://localhost:3000 in your browser to explore your project.

For more detailed instructions, check out the README in the GitHub repository.

Documentation

Learn how to use the prebuilt components, hooks, and utilities included in this starter. Below are some essential parts of the system to help you quickly integrate and expand upon this project.

🔹 Fetching Data

import { useClientFetch } from "@/hooks/useClientFetch";

const Posts = () => {
  const { data, isLoading } = useClientFetch("posts", "posts");

  if (isLoading) return <p>Loading...</p>;

  return (
    <ul>
      {data?.map((post) => (
        <li key={post.id}>{post.name}</li>
      ))}
    </ul>
  );
};

🔹 Mutations

import { useClientMutate } from "@/hooks/useClientMutate";

const AddPost = () => {
  const mutation = useClientMutate("posts", "insert");

  const handleSubmit = async () => {
    mutation.mutate({ id: Date.now(), name: "New Post" });
  };

  return <button onClick={handleSubmit}>Add Post</button>;
};

🔹 Advanced Hooks Usage

The starter includes powerful hooks to handle complex use cases. Here are some advanced examples:

🔸 Fetching Data with Filters

const FilteredUsers = () => {
  const { data, isLoading } = useClientFetch(
    "filtered-users", // key
    "users", // table name
    5000, // cache time
    (query) => query.eq("role", "admin") // Supabase query filter
  );
  
  if (isLoading) return <p>Loading...</p>;
  
  return (
    <ul>
      {data?.map((user) => (
        <li key={user.id}>{user.name} ({user.role})</li>
      ))}
    </ul>
  );
};

🔸 Mutating with Optimistic Updates

import { useClientMutate } from "@/hooks/useClientMutate";
import { useQueryClient } from "@tanstack/react-query";

const queryClient = useQueryClient();
const mutation = useClientMutate("posts", "update", {
  onMutate: async (newData) => {
    await queryClient.cancelQueries(["posts"]);
    const previousData = queryClient.getQueryData(["posts"]);
    queryClient.setQueryData(["posts"], (oldData) =>
      oldData?.map(post => (post.id === newData.id ? { ...post, ...newData } : post))
    );
    return { previousData };
  },
  onError: (err, newData, context) => {
    queryClient.setQueryData(["posts"], context.previousData);
  },
  onSettled: () => {
    queryClient.invalidateQueries(["posts"]);
  },
});
mutation.mutate({ id: 1, name: "Updated Post" });

🔸 Subscribing to Real-Time Data

import { useEffect, useState } from "react";
import { createClient } from "@/supabase/client;

const supabase = createClient()

const [messages, setMessages] = useState([]);

useEffect(() => {
  const subscription = supabase
    .from("messages")
    .on("INSERT", (payload) => {
      setMessages((prev) => [...prev, payload.new]);
    })
    .subscribe();

  return () => {
    supabase.removeSubscription(subscription);
  };
}, []);