Migrating Remix loader data to a NextJs server component

Getting Pocketbase data into a component

Pocketbase setup

We will fetch our posts from pocketbase.

// pocketbaseUtils.ts
import PocketBase from "pocketbase";

// next
const pb = new PocketBase(process.env.POCKETBASE_URL);

// remix
// const pb = new PocketBase(import.meta.env.VITE_PB_URL)

export const getPb = () => {
  return pb;
};

The Remix v2 (vite) way

In Remix we export a loader that gets the slug url segment from its params object. We return the record from that function before calling it in the component via useLoaderData.

// remix --> blog_.$slug.tsx

import { getPb } from "../data/pocketbaseUtils"
import { LoaderFunction, LoaderFunctionArgs } from "@remix-run/node"
import { useLoaderData } from "@remix-run/react"

export const loader: LoaderFunction = async ({
  params,
}: LoaderFunctionArgs) => {
  const pb = getPb()
  const record = await pb
    .collection("posts")
    .getFirstListItem(`slug="${params.slug}"`, {})
  return record
}

const Post = () => {
  const record = useLoaderData<typeof loader>()
  // ...

The Next v15 RSC way

In a server component, we can make the Post itself an async function, which will depend on the Params promise resolving to get our slug url segment.

From there we can call the database directly and use its data in our markup, all in one go.

// nextJs --> blog/[slug]/page.tsx
import React from 'react'
import { getPb } from '@/app/data/pocketbaseUtils'

type Params = Promise<{ slug: string[] }>

const Post = async ({ params }: { params: Params }) => {
  const { slug } = await params
  const pb = getPb()
  const record = await pb
    .collection('posts')
    .getFirstListItem(`slug="${slug}"`, {})

  return (
    // ... server component
    )

I’ve moved this site to Next 15 in part due to difficulties running animations in Remix v2, but I’m enjoying this RSC data loading pattern for simple reads from Pocketbase.