bloomreach / spa-sdk

Apache License 2.0
17 stars 14 forks source link

Serverside rendering #38

Open ennair opened 3 months ago

ennair commented 3 months ago

Hi, In the current example of Next, the highest component BrxApp (https://github.com/bloomreach/spa-sdk/blob/main/examples/next/components/BrxApp.tsx#L1) is a clientside component, because of the use of BrPage which is a client component. All components underneath therefore will also be clientside components, also because they need to get the page and component contexts. Is it possible to set it up serverside? So we can render our components more serverside? There are a couple of benefits of rendering it serverside: https://nextjs.org/docs/app/building-your-application/rendering/server-components#benefits-of-server-rendering.

ksalic commented 3 months ago

@hachok would you be able to answer this?

hachok commented 3 months ago

The highest component is the Page, which allows you to hydrate and render the page on the server side. Using the composition pattern, you can compose client and server components. More information about this can be found here: Interleaving Server and Client Components

ennair commented 3 months ago

I get what you mean, only this is not working for the mapping components which you provide to BrPage. If you add components via children of BrPage you will be able to run them serverside, but then the question remains how to get the page data without context. Because when using context it will have to be a client component again. I have made a small example.

app/[[...route]]/page.tsx

import BrAllPages from '../../components/BrAllPages';
import { getBrPageModel } from '../../utils/https-utils';
import { headers } from 'next/headers';

export default async function Page({ searchParams }) {
  const { ...query } = searchParams;

  const headersList = headers();
  const pathname = headersList.get('x-pathname');

  const { page, ...props } = await getBrPageModel(
    pathname,
    'nl',
    query,
    headersList,
    {},
  );

  return <BrAllPages configuration={props} page={page} />;
}

components/BrAllPages/BrAllPages.tsx (serverside component)

import BrPageWrapper from '../../components/layout/BrPageWrapper';
import BrTextViaChildren from '../BrTextViaChildren';

export default function AllPages({ configuration, page }) {
  console.log('BrAllPages');

  return (
    <BrPageWrapper page={page} configuration={configuration}>
      <BrTextViaChildren />
    </BrPageWrapper>
  );
}

As you can see I gave the BrPageWrapper component (clientside component) a child BrTextViaChildren component (serverside component). This works, but how do I get the page data from BrPageWrapper without using context? Because when I start using context in BrTextViaChildren it has to be a clientside component.

components/BrTextViaChildren/BrTextViaChildren (serverside component)

export function BrTextViaChildren() {
  console.log('BrTextChildren');

  return (
    <div className='component-wrapper content-small-wrapper'>
      <p>Text via children, but how?</p>
    </div>
  );
}

components/BrPageWrapper/BrPageWrapper (clientside component)

'use client';

import { BrComponent, BrPage } from '@bloomreach/react-sdk';
import axios from 'axios';
import BrTextViaMapping from '../BrTextViaMapping';

type BrPageWrapperProps = {
  page: any;
  configuration: any;
  children: any;
  metaData?: any;
};

export function BrPageWrapper({
  page,
  configuration,
  children,
}: BrPageWrapperProps) {
  console.log('BrPageWrapper');

  const mapping = {
    'Inhoud': BrTextViaMapping,
  };

  return (
    <div id='p1o-app' className='page-wrapper'>
      <BrPage
      configuration={{ ...configuration, httpClient: axios }}
      mapping={mapping}
      page={page}
    >
      <BrComponent path='main-menu' />

      <BrComponent path='information-area' />
      {children}

    </BrPage>
    </div>
  );
}

components/BrTextViaMapping/BrTextViaMapping (because it is used in via de mapping of BrPage it automatically becomes a clientside component)

import { BrProps } from '@bloomreach/react-sdk';
import { Content, Page } from '@bloomreach/spa-sdk';

export function BrTextViaMapping(props: BrProps) {
  const page: Page = props.page;
  const document: Content = page?.getDocument();

  console.log('BrTextViaMapping');

  const content = document?.getData()?.introduction?.value;

  return (
    <div className='component-wrapper content-small-wrapper'>
      {content && <p>{content}</p>}
    </div>
  );
}

I tried it via the children, but then I do not know how to get the page data from the BrPage. I also have tried it via the mapping, then I am able to get the page data, but the component automatically becomes a clientside component and not serverside component.

So how am I able to make a component, which can access the Bloomreach page data and is serverside rendered?

hachok commented 2 months ago

The client components are still rendered on the server side for the initial HTML load. Here is an article that explains the concept: Why do Client Components get SSR'd to HTML?. Having client components in mapping should provide HTML page from the server side. More information about this can be found here: How are Client Components Rendered?

ennair commented 2 months ago

Yes, but still a part of it is still done on the client as you can also see in the links you send. I mean for components without any interactivity needed, like RichText components, of which a page often has the most of, wouldn't it be nicer if we could have them as server components instead of client components?

hachok commented 2 months ago

Sure! To summarize and avoid confusion, the server-side rendering works, and it's possible to get a fully prerendered HTML page from the server. However, having only client components in the mapping excludes the benefits of server components. We'll consider changing @bloomreach/react-sdk so that it'd be possible to have RSC in the mapping. Thanks for your input!

cwmrowe commented 1 week ago

Hi @hachok while we wait for React Server Components support, is there documentation of how to use Bloomreach CMS in a pure NodeJS environment? i.e one that does not require JS in the browser?