Friday, April 18, 2025

Getting Started with the Supabase UI Library

The Supabase UI library, built on ShadCN, simplifies connecting Supabase to React-based applications. This guide walks through setting up a Next.js project, installing the UI library, and implementing authentication and file uploads with minimal hassle. Let’s dive in.

Setting Up the Next.js Project

Start by creating a Next.js application using NPX. You can use any React-based framework like Remix, React Router, or TanStack Start, but we’ll stick with Next.js for this example.

npx create-next-app@latest ui-library

Accept the default settings, navigate to the project directory, and open it in VS Code. Run the development server to view the default Next.js template at localhost:3000:

cd ui-library
npm run dev

You’ll see the standard Next.js landing page in your browser.

Installing ShadCN

The Supabase UI library is built on ShadCN, a collection of accessible, well-styled UI components. To use it, first install ShadCN in your project. Head to the ShadCN documentation, grab the initialization command for Next.js, and run it in your terminal:

npx shadcn-ui@latest init

Choose “neutral” as the base color and use the --force flag to bypass dependency issues with React 19:

npx shadcn-ui@latest init --force

To test ShadCN, add a button component. Copy the button installation command from the ShadCN docs and run it:

npx shadcn-ui@latest add button --force

In app/page.tsx, simplify the default template to:

import { Button } from "@/components/ui/button";

export default function Home() {
  return (
    <div className="flex min-h-screen flex-col items-center justify-center">
      <main>
        <Button>Click me</Button>
      </main>
    </div>
  );
}

To enable dark mode, update app/layout.tsx by adding the dark class to the body:

<body className="dark">{children}</body>

Refresh the browser to see the styled button in dark mode, avoiding the default light mode flash.

Installing the Supabase UI Library

The Supabase UI library extends ShadCN’s philosophy: reusable, customizable components with code that lives in your codebase. To install it, copy the installation command from the Supabase UI library docs and run it with the --force flag:

npx supabase-ui@latest init --force

This sets up authentication pages (login, signup, etc.) under app/auth. To connect to Supabase, create a .env.local file in the project root with your Supabase project’s URL and anon key. If you don’t have a Supabase project, create one:

  1. Go to database.new on Supabase.
  2. Sign up or log in.
  3. Create a project (e.g., “super-ui”) under your organization.
  4. Choose a region close to your users (e.g., Oceania - Sydney).
  5. Generate a password and create the project.

Once the project is ready, copy the URL and anon key from the “Connect” section in the Supabase dashboard and add them to .env.local:

NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key

Restart the dev server. Visiting localhost:3000 should redirect to /auth/login.

Implementing Authentication

The Supabase UI library makes authentication straightforward. Test the signup flow:

  1. On the login page, click “Sign up.”
  2. Enter an email (e.g., john@supabase.com) and a secure password.
  3. Submit the form to receive a confirmation email.
  4. Confirm the email and log in with the same credentials.

After logging in, you’re redirected to a protected page (/protected) displaying a “Hello, John” message. The protected page’s code, located in app/protected/page.tsx, creates a Supabase client, checks for an authenticated user, and redirects to the login page if none exists:

import { createClient } from "@/utils/supabase/server";
import { redirect } from "next/navigation";

export default async function ProtectedPage() {
  const supabase = createClient();
  const { data: { user }, error } = await supabase.auth.getUser();

  if (error || !user) {
    redirect("/auth/login");
  }

  return <div>Hello, {user.email}</div>;
}

To display more user data, modify the page to pretty-print the user object:

import { createClient } from "@/utils/supabase/server";
import { redirect } from "next/navigation";

export default async function ProtectedPage() {
  const supabase = createClient();
  const { data: { user }, error } = await supabase.auth.getUser();

  if (error || !user) {
    redirect("/auth/login");
  }

  return (
    <div className="flex flex-col items-center">
      <div>Hello, {user.email}</div>
      <pre>{JSON.stringify(user, null, 2)}</pre>
    </div>
  );
}

Refresh the browser to see detailed user information, such as the email and authentication status. The library handles forgot-password flows, email confirmation, and social logins (e.g., GitHub, Google), all customizable since the code resides in your project.

Adding File Uploads

File uploads can be complex, but the Supabase UI library simplifies them with a dropzone component. Install it:

npx supabase-ui@latest add dropzone --force

Create a new page at app/upload/page.tsx:

import FileUploadDemo from "@/components/upload-form";

export default function UploadPage() {
  return (
    <div className="flex min-h-screen flex-col items-center justify-center">
      <main>
        <FileUploadDemo />
      </main>
    </div>
  );
}

The dropzone component, located in components/upload-form.tsx, is preconfigured. Update its settings to use a Supabase storage bucket named “images” and restrict uploads to images in a “profiles” folder:

import { Dropzone } from "@/components/ui/dropzone";

export default function FileUploadDemo() {
  return (
    <Dropzone
      bucket="images"
      path="profiles"
      accept="image/*"
      maxFiles={1}
      maxSize={5 * 1024 * 1024} // 5MB
    />
  );
}

Before uploading, create the “images” bucket in Supabase:

  1. In the Supabase dashboard, go to “Storage.”
  2. Create a bucket named “images.”

By default, buckets have strict security policies. Create policies to allow authenticated users to read and write files:

  1. In “Storage > Policies,” create a policy for the “images” bucket:
    • Use the template for authenticated users.
    • Enable “SELECT” and “INSERT” operations.
    • Save the policy.
  2. Create two policies for storage.objects:
    • Read access: Allow “SELECT” for authenticated users (expression: true).
    • Write access: Allow “INSERT” for authenticated users (expression: true).

Now, visit /upload, log in if prompted, and drag an image file (e.g., profile.png) into the dropzone. The upload should succeed. Check the Supabase dashboard to confirm the file appears in the “images” bucket under the “profiles” folder.

Customizing Components

Both ShadCN and Supabase UI components are fully customizable because their code lives in your project. For example, to modify the button’s appearance, edit components/ui/button.tsx. To add social logins to the authentication flow, update the relevant auth components in app/auth. This approach ensures flexibility without relying on abstracted libraries.

What’s Next?

The Supabase UI library streamlines authentication, file storage, and real-time subscriptions. Future additions could include components for user profiles, real-time chat, or advanced form handling. Since the code is yours, you can extend it to fit your needs—whether that’s adding crypto-based sign-ins or custom UI tweaks.

For inspiration, explore how far you can push Supabase with its AI assistant, or experiment with other ShadCN components like calendars or modals. The library’s accessibility, great UX, and local code ownership make it a powerful tool for React developers.

Keep building, and let us know what features you’d like to see next.

0 comments:

Post a Comment