← go back

how to track blog post views with supabase

481 views

if you're building a blog with next.js and want to track the number of views each post gets, you can easily implement a view counter using supabase. this tutorial will walk you through the steps to set it up

prerequisites

before we begin, make sure you have the following:

npx create-next-app@latest my-blog

1. create a new table in supabase

go to your supabase dashboard and create a new table called views with the following columns:

you can either generate it with the sql editor or use the following sql query:

create table views (
  id uuid primary key default gen_random_uuid(),
  slug text unique,
  count integer default 1
);

2. create database function

go to the sql editor in your supabase dashboard and create a new function called update_views with the following code:

CREATE OR REPLACE FUNCTION update_views(input_slug text)
RETURNS public.views AS $$
DECLARE
    updated_row public.views;
BEGIN
    -- Try to update the existing row
    UPDATE public.views
    SET count = count + 1
    WHERE slug = input_slug
    RETURNING * INTO updated_row;

    -- If no row was updated, insert a new row
    IF NOT FOUND THEN
        INSERT INTO public.views (slug, count)
        VALUES (input_slug, 1)
        RETURNING * INTO updated_row;
    END IF;

    RETURN updated_row;
END;
$$ LANGUAGE plpgsql;

this function will increment the view count for a given slug or create a new row if it doesn't exist. this will allow us to perform the view count increment in a single database call, instead of a separate read and write operation

3. add supabase to your next.js app

npm i @supabase/ssr

you can also use the @supabase/supabase-js package if you prefer, in this tutorial we'll use the @supabase/ssr package since it provides server-side rendering support

create a new file called server.ts in your project, inside the utils/supabase folder, with the following code:

import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";

export async function createClient() {
  const cookieStore = await cookies();

  return createServerClient(process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, {
    cookies: {
      getAll() {
        return cookieStore.getAll();
      },
      setAll(cookiesToSet) {
        try {
          cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options));
        } catch {
          // The `setAll` method was called from a Server Component.
          // This can be ignored if you have middleware refreshing
          // user sessions.
        }
      },
    },
  });
}

make sure to add your supabase url and anon key to your .env.local file

4. create a view-counter component

create a new file called view-counter.tsx inside the components folder in your project with the following code:

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

export async function ViewCounter({ slug }: { slug: string }) {
  const className = "text-xs font-mono text-gray-500";

  if (process.env.NODE_ENV === "development") {
    // Skip database interactions in development mode
    return <p className={className}>0 views (development mode)</p>;
  }

  const supabase = await createClient();

  // Call the `update_views` db function
  const { data: views, error } = await supabase.rpc("update_views", { input_slug: slug });

  if (error) {
    return <p className={className}>Error fetching views.</p>;
  }

  return <p className={className}>{views?.count || 1} views</p>;
}

this component will increment the view count for the given slug and display the number of views

5. import the view-counter component in your blog posts

use the component in your blog post page like this:

import { ViewCounter } from "@/components/view-counter";

export default function Post({ post }) {
  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
      <ViewCounter slug={post.slug} />
    </div>
  );
}

that's it! now you have a view counter for your blog posts using supabase

hope you found this tutorial helpful! if you have any questions or feedback, feel free to reach out to me on x