A sneak peek at the Supabase and Next.js RSC SaaS kit

A behind the scenes look at how we built the Supabase and Next.js RSC SaaS Starter.

9 min read
Cover Image for A sneak peek at the Supabase and Next.js RSC SaaS kit

As you may have seen, we are working on a new SaaS kit based on Supabase and Next.js 13 using React Server Components and the new app directory. We are excited to share a behind-the-scenes look at how we built the kit.

A quick recap: what is a SaaS kit?

Makerkit's kits are a collection of templates that you can use to build your own SaaS products.

The goal of the kits is to provide the building blocks that you need to build your own SaaS product and to help you get started quickly, such as a landing page, authentication, Stripe payments, emails, multi-tenancy architecture based on organizations, user invites, and more.

The kits are built using the latest technologies and are designed to be easy to customize and extend to fit your needs.

The Supabase and Next.js RSC SaaS kit

The Supabase and Next.js RSC SaaS kit is a new kit that we are working on. It is based on Supabase, Next.js 13 React Server Components and the new app directory.

A rewrite of the Supabase and Remix SaaS kit

It's mostly a rewrite of the Supabase and Remix SaaS kit so the two kits are very similar. While competing, the two frameworks are extremely similar, and the migration was mostly a matter of changing the framework-specific code.

In short, both frameworks are great and you can't go wrong with either one. It's important to remember that while Remix is production-ready, (at the time of writing) the Next.js app directory is still in beta.

The Next.js app directory is in beta

Therefore, this kit is going to be a beta kit as well until the Next.js app directory is out of beta.

Enter React Server Components

The most striking difference that you will find in the new kit is the use of React Server Components.

The new Next.js app directory leverages React Server Components by default: Server components are rendered on the server and then sent to the client as a serialized stream.

What the above means is that the client can render the component without having to download the JavaScript bundle: as you can imagine, this allows Next.js to deliver a much faster initial page load.

Did you notice performance gains?

Generally speaking, yes.

But the performance gains don't come for free: your components need to be written in a way that allows them to be rendered on the server, which means they will mostly be data-fetching and rendering components, and no interactivity.

Interactive components (client components) will be rendered using SSR and hydrate on the client just like you're used to the traditional Next.js /pages directory.

When you're able to write your components in a way that allows them to be rendered on the server, combined with carefully written laoding.tsx loading handlers, you can take advantage of the performance gains that server components unlock and provide a much smoother user experience than possible today.

The new Next.js app directory

The new Makerkit app directory will look like the following tree:

- app - (app) - components - dashboard - page.tsx - settings - organization - page.tsx - profile - page.tsx - subscription - page.tsx - layout.tsx - loading.tsx - (site) - about - page.tsx - faq - page.tsx - pricing - page.tsx - layout.tsx - loading.tsx - page.tsx - api - organizations - route.ts - stripe - checkout - route.ts - auth - components - password-reset - page.tsx - sign-in - page.tsx - sign-up - page.tsx - layout.tsx - loading.tsx - invite - [code] - components - layout.tsx - onboarding - page.tsx - components.css - globals.css - layout.tsx - loading.tsx

If you're not familiar with the Next.js app directory, here is a quick overview:

  • pages are defined using the special filename page.tsx
  • layouts are defined using the special filename layout.tsx
  • loading handlers are defined using the special filename loading.tsx
  • directories will create a new route, for example, app/dashboard will create the route /app/dashboard
  • directories using parenthesis are "pathless", .e.g. (app) will not create the route /app, but will start from the root of the app directory, for example, (app)/dashboard will create the route /dashboard. The same goes for (site)

Pathless directories

Pathless directories are useful for defining routes that are not part of your app: for example, the (site) directory is used to define the routes for your landing page, pricing page, and more. Instead, the app directory is used to define the routes for your app, i.e. the ones behind authentication.

Data-Fetching Layouts

What makes these particularly useful is the fact we can define data-fetching layouts that can fetch the data that is common to the pages in the directory: for example, it may not be useful to fetch the current organization in the site pages, but it is useful to fetch the current organization in the app pages.

Or, for example, we want to ensure that logged-in users can't access the auth pages, but we don't want to do the same for the app pages. By using layouts in such a way that we can control both data-fetching and authentication, we can write our pages in a way that is more declarative and easier to reason about.

Co-locating components

Another interesting feature of the Next.js app directory is the ability to co-locate components with your pages. As you can see, we define the directory components close to where they're used, such as near the auth pages.

In this way, we can easily see which components are used by which pages, and we can easily move them around if we need to.

Given we have shared components, we can also define a components directory at the root of the app directory, which will be shared across all pages.

The good parts of the Next.js app directory

The Next.js app directory is a great way to organize your Next.js app. In fact, I quite enjoyed the experience of using it.

Layouts

Layouts allow us to define components that wrap your pages and provide a consistent look and feel across your app.

Additionally, layouts also help you to fetch data that is common to all pages in your app, such as the current user, the current organization, and more.

Data loading

We can use Layouts for fetching data that is common to all pages in its directory. For example, we can fetch the current user in the auth directory, and we can fetch the current organization in the app directory, and so on.

In Makerkit, we define "data loaders" such as loadAppData and loadAuthData that are used to fetch the data that is common to all pages in the app and auth directories, respectively.

These also perform some validation such as ensuring that the user is authenticated and that the user is part of the organization, and so on. In such cases, we define a redirect property that tells the layouts not to proceed and redirect somewhere instead.

Let's see the loader for the authentication pages:

import getSupabaseServerClient from '~/core/supabase/server-client'; import configuration from '~/configuration'; const loadAuthPageData = async () => { try { const client = getSupabaseServerClient(); const { data: { session }, } = await client.auth.getSession(); if (session) { return { redirect: true, destination: configuration.paths.appHome, }; } return {}; } catch (e) { return {}; } }; export default loadAuthPageData;

Now, let's use the loader above in the auth layout:

import { use } from 'react'; import { redirect } from 'next/navigation'; import loadAuthPageData from '~/lib/server/loaders/load-auth-page-data'; import Logo from '~/core/ui/Logo'; import I18nProvider from '~/i18n/I18nProvider'; function AuthLayout({ children }: React.PropsWithChildren) { const data = use(loadAuthPageData()); if ('redirect' in data && data.destination) { return redirect(data.destination); } return ( <I18nProvider> <div className={ 'flex h-screen flex-col items-center justify-center space-y-4 md:space-y-8 lg:bg-gray-50 dark:lg:bg-black-700' } > <div> <Logo /> {children} </div> </div> </I18nProvider> ); } export default AuthLayout;

As you can see, we:

  1. fetch the data
  2. if the data contains a redirect property, we redirect to the destination (for example, when the user is already logged in)
  3. otherwise, we render the children (such as the sign-in page, etc.)

The other layouts will work in a very similar way.

The new Next.js Route handlers

Next.js has also released an alternative way to define API routes, using the new Route handlers. This new API is still experimental and will replace the "old" pages/api API handlers.

Let's take a quick look at the new API handlers. We have added a new directory called api that contains the API handlers. To define a route, we create a directory within api and create a file called route.ts.

For example, we create a route handler for the /api/stripe/checkout route:

app/api/stripe/checkout/route.ts
export async function POST( req: Request ) { const payload = await req.text(); /// handle webhook here return NextResponse.json({ success: true, }); }

As you can see we can define the method handlers by simply exporting the method name using its uppercase form. For example, we can define a GET handler by exporting a GET function.

export async function GET( req: Request ) { const data = await getData(); return NextResponse.json(data); }

While I have created a folder named api which looks a lot like the pages/api directory, it is important to notice that you can co-locate API handlers with your pages, for example, you can create a checkout directory in the app directory and create a route.ts file there and execute a request against the /app/checkout route.

In my opinion, it can still be useful to define API handlers in a separate directory, such as the api directory, as it allows you to define API handlers that are not part of your app, such as the stripe API handlers. This is just my preference at this time, and it can likely change in the future.

Difficulties with the Next.js app directory

While everything has been generally smooth, there have been a few difficulties with the Next.js app directory.

i18n

At this time, the biggest difficulty with the Next.js app directory has been i18n using i18next. I have been able to get it working, but it has been a bit of a struggle.

The examples online are either lacking or simply don't work, and I have had to spend a lot of time trying to figure out how to get them working.

Mutations

Next.js is still working on mutations, which will likely allow us to define mutations in the same way we define API handlers, and automatically refresh the loaders when the mutations are executed, similar to how Remix works.

To work it around for the time being, mutations are executed using fetch requests wrapped with useMutation from swr, and manually calling router.refresh when the mutation succeeds. Ideally, we will be able to remove the router.refresh call once mutations are supported.

Conclusion

The Next.js Supabase SaaS kit is slated to be released in March, and I am very excited about it. Do you have any questions about the kit? Let me know!

If you want, sign up for the newsletter to be notified when the kit is released.

Ciao!



Read more about Changelog

Cover Image for Introducing Makerkit Turbo: faster, simpler and more powerful SaaS Boilerplate

Introducing Makerkit Turbo: faster, simpler and more powerful SaaS Boilerplate

4 min read
I am excited to announce Makerkit Turbo - the next generation of our SaaS boilerplate. It's a tremendous upgrade over the previous version, with a focus on speed, simplicity, and power.
Cover Image for Announcing the AI Blog Writer SaaS Template

Announcing the AI Blog Writer SaaS Template

3 min read
We're excited to announce the launch of our latest SaaS template, the AI Blog Writer app template. This template is a great starting point for building a product to write SEO optimized blog posts using AI.
Cover Image for Announcing the AI Chat with PDF SaaS Template

Announcing the AI Chat with PDF SaaS Template

3 min read
We're excited to announce the launch of our latest SaaS template, the Chat with PDF app template. This template is a great starting point for building a product that allows your customers to create chatbots that can answer questions and search through PDF documents.
Cover Image for Announcing the AI Chatbot SaaS Template

Announcing the AI Chatbot SaaS Template

3 min read
We're excited to announce the launch of our latest SaaS template, the AI Chatbot SaaS Template. This template is a great starting point for building a customer support chatbot SaaS product.
Cover Image for Announcing Premium Codebase Access

Announcing Premium Codebase Access

4 min read
We are excited to announce that we are now offering Premium Codebase Access to all our users on the Teams Plan.
Cover Image for Announcing the new Onboarding Flow!

Announcing the new Onboarding Flow!

2 min read
The latest update to the SaaS Starter Kits is here! This update includes changes to the Onboarding Flow after sign-up.