# Learn how to load data from the Supabase database

> In this page we learn how to load data from the Supabase database and display it in our Next.js application.

*Canonical: https://makerkit.dev/docs/next-supabase-turbo/development/loading-data-from-database*

---

Now that our database supports the data we need, we can start loading it into our application. We will use the `@makerkit/data-loader-supabase-nextjs` package to load data from the Supabase database.

Please check the [documentation](https://github.com/makerkit/makerkit/tree/main/packages/data-loader/supabase/nextjs) for the `@makerkit/data-loader-supabase-nextjs` package to learn more about how to use it.

This nifty package allows us to load data from the Supabase database and display it in our server components with support for pagination.

In the snippet below, we will:

1. Load the user's workspace data from the database. This allows us to get the user's account ID without further round-trips because the workspace is loaded by the user layout.
2. Load the user's tasks from the database.
3. Display the tasks in a table.
4. Use a search input to filter the tasks by title.

Let's take a look at the code:

```tsx
import { use } from 'react';

import { ServerDataLoader } from '@makerkit/data-loader-supabase-nextjs';

import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { Button } from '@kit/ui/button';
import { Heading } from '@kit/ui/heading';
import { If } from '@kit/ui/if';
import { Input } from '@kit/ui/input';
import { PageBody } from '@kit/ui/page';
import { Trans } from '@kit/ui/trans';

import { getTranslations } from 'next-intl/server';

import { TasksTable } from './_components/tasks-table';
import { UserAccountHeader } from './_components/user-account-header';
import { loadUserWorkspace } from './_lib/server/load-user-workspace';

interface SearchParams {
  page?: string;
  query?: string;
}

export const generateMetadata = async () => {
  const t = await getTranslations('account');
  const title = t('homePage');

  return {
    title,
  };
};

async function UserHomePage(props: { searchParams: Promise<SearchParams> }) {
  const client = getSupabaseServerClient();
  const { user } = use(loadUserWorkspace());

  const searchParams = await props.searchParams;
  const page = parseInt(searchParams.page ?? '1', 10);
  const query = searchParams.query ?? '';

  return (
    <>
      <UserAccountHeader
        title={<Trans i18nKey={'common.homeTabLabel'} />}
        description={<Trans i18nKey={'common.homeTabDescription'} />}
      />

      <PageBody className={'space-y-4'}>
        <div className={'flex items-center justify-between'}>
          <div>
            <Heading level={4}>
              <Trans i18nKey={'tasks.tasksTabLabel'} defaults={'Tasks'} />
            </Heading>
          </div>

          <div className={'flex items-center space-x-2'}>
            <form className={'w-full'}>
              <Input
                name={'query'}
                defaultValue={query}
                className={'w-full lg:w-[18rem]'}
                placeholder={'Search tasks'}
              />
            </form>
          </div>
        </div>

        <ServerDataLoader
          client={client}
          table={'tasks'}
          page={page}
          where={{
            account_id: {
              eq: user.id,
            },
            title: {
              textSearch: query ? `%${query}%` : undefined,
            },
          }}
        >
          {(props) => {
            return (
              <div className={'flex flex-col space-y-8'}>
                <If condition={props.count === 0 && query}>
                  <div className={'flex flex-col space-y-2.5'}>
                    <p>
                      <Trans
                        i18nKey={'tasks.noTasksFound'}
                        values={{ query }}
                      />
                    </p>

                    <form>
                      <input type="hidden" name={'query'} value={''} />

                      <Button variant={'outline'} size={'sm'}>
                        <Trans i18nKey={'tasks.clearSearch'} />
                      </Button>
                    </form>
                  </div>
                </If>

                <TasksTable {...props} />
              </div>
            );
          }}
        </ServerDataLoader>
      </PageBody>
    </>
  );
}

export default UserHomePage;
```

Let's break this down a bit:

1. We import the necessary components and functions.
2. We define the `SearchParams` interface to type the search parameters.
3. We define the `generateMetadata` function to generate the page metadata.
4. We define the `UserHomePage` component that loads the user's workspace and tasks from the database.
5. We define the `ServerDataLoader` component that loads the tasks from the database.
6. We render the tasks in a table and provide a search input to filter the tasks by title.
7. We export the `UserHomePage` component.

### Displaying the tasks in a table

Now, let's show the tasks table component:

```tsx
'use client';

import Link from 'next/link';

import { ColumnDef } from '@tanstack/react-table';
import { Pencil } from 'lucide-react';
import { useTranslations } from 'next-intl';

import { Button } from '@kit/ui/button';
import { DataTable } from '@kit/ui/enhanced-data-table';

import { Database } from '~/lib/database.types';

type Task = Database['public']['Tables']['tasks']['Row'];

export function TasksTable(props: {
  data: Task[];
  page: number;
  pageSize: number;
  pageCount: number;
}) {
  const columns = useGetColumns();

  return (
    <div>
      <DataTable {...props} columns={columns} />
    </div>
  );
}

function useGetColumns(): ColumnDef<Task>[] {
  const t = useTranslations('tasks');

  return [
    {
      header: t('task'),
      cell: ({ row }) => (
        <Link
          className={'hover:underline'}
          href={`/home/tasks/${row.original.id}`}
        >
          {row.original.title}
        </Link>
      ),
    },
    {
      header: t('createdAt'),
      accessorKey: 'created_at',
    },
    {
      header: t('updatedAt'),
      accessorKey: 'updated_at',
    },
    {
      header: '',
      id: 'actions',
      cell: ({ row }) => {
        const id = row.original.id;

        return (
          <div className={'flex justify-end space-x-2'}>
            <Link href={`/home/tasks/${id}`}>
              <Button variant={'ghost'} size={'icon'}>
                <Pencil className={'h-4'} />
              </Button>
            </Link>
          </div>
        );
      },
    },
  ];
}
```

In this snippet, we define the `TasksTable` component that renders the tasks in a table. We use the `DataTable` component from the `@kit/ui/enhanced-data-table` package to render the table.

We also define the `useGetColumns` hook that returns the columns for the table. We use the `useTranslations` hook from `next-intl` to translate the column headers.
