mike.jarosch.dev

Using AppPlaceholder with Server Components and Client Components

Cover Image for Using AppPlaceholder with Server Components and Client Components

Update

Sitecore has updated their documentation to include this.

Original Post

Switching to the App Router allows for Sitecore components to be either Server or Client Components. This change brings a new placeholder component, the AppPlaceholder. There are a few considerations for using AppPlaceholder not covered in the documentation.

Server vs Client Components

The AppRouter brings with it the ability for components to be either a server component or a client component.

A server component is rendered on the server and not included in the client bundle, shrinking the JavaScript payload. The trade off is that server components cannot use hooks (useState, useEffect, ...) or browser APIs. server components are the default.

A client component is rendered on the server initially and then hydrated and run in the browser. They are included in the client bundle and can use hooks and browser APIs. client components are opted into by using the "use client" directive.

AppPlaceholder in components

Both server and client components can use AppPlaceholder. The difference is what is passed to the componentMap parameter. The server component imports the component map from component-map.ts and client component needs to import the component map from component-map.client.ts. The difference between these two maps is that component-map.ts includes server and client components while component-map.client.ts only includes client components.

This means that AppPlaceholders in client components can only contain other client components. If a content author is allowed to place a server component into a placeholder rendered in a client component, it will not be found in the component map.

The Workaround

The workaround is to convert the client component into a client helper that is wrapped by a server component. First we pull the client component code, including the "use client" into a separate helper react component file. We can then use the AppPlaceholder and pass it to a helper.

Example

First, we need to create the proxy that copies the parameters into the headers.

"use client"

import { AppPlaceholder } from '@sitecore-content-sdk/nextjs';
import { componentWithContextProps } from 'lib/component-props';
import componentMap from '.sitecore/component-map.client';

export function SitecoreComponent({ page, rendering }: componentWithContextProps) {
  ...
  return (
    ...
    <AppPlaceholder
      page={page}
      componentMap={componentMap}
      name="placeholder-name"
      rendering={rendering}
    />
    ...
  );
}

This is broken into two files.

"use client"

export function ClientHelper({ placeholder }) {
  ...
  return (
    ...
    {placeholder}
    ...
  );
}
import { AppPlaceholder } from '@sitecore-content-sdk/nextjs';
import { componentWithContextProps } from 'lib/component-props';
import { ClientHelper } from '...';
import componentMap from '.sitecore/component-map';

export function SitecoreComponent({ page, rendering }: componentWithContextProps) {
  return (
    <ClientHelper placeholder={
      <AppPlaceholder
        page={page}
        componentMap={componentMap}
        name="placeholder-name"
        rendering={rendering}
      />}>
  );
}

Conclusion

The server component handles the rendering of the placeholder and the client helper handles the client interactivity.