Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serverside rendering #38

Open
ennair opened this issue Jun 25, 2024 · 11 comments
Open

Serverside rendering #38

ennair opened this issue Jun 25, 2024 · 11 comments

Comments

@ennair
Copy link

ennair commented Jun 25, 2024

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
Copy link

ksalic commented Jul 4, 2024

@hachok would you be able to answer this?

@hachok
Copy link
Collaborator

hachok commented Jul 4, 2024

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
Copy link
Author

ennair commented Jul 5, 2024

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
Copy link
Collaborator

hachok commented Jul 8, 2024

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
Copy link
Author

ennair commented Jul 8, 2024

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
Copy link
Collaborator

hachok commented Jul 8, 2024

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
Copy link

cwmrowe commented Sep 23, 2024

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?

@cwmrowe
Copy link

cwmrowe commented Dec 30, 2024

@hachok any update on the support for React Server Components, or details on how we can use Bloomreach with server side rendered pages in NodeJS. This is preventing our upgrade from NextJS pages to NextJS App Router. https://nextjs.org/docs/app

@joerideg
Copy link
Contributor

Hi @cwmrowe ,
We have raised this internally and we are planning to rewrite the SDK into functional components as class components are not supported in RSC. Then we will also take a look at changing the way the context is used, such that you can use the context separately from BrPage and thus align more with RSC usage. We will post updates here as we make progress.

@cwmrowe
Copy link

cwmrowe commented Jan 16, 2025

Hi @joerideg. One of the benefits of RSCs, or indeed server rendering in general, is that we need fewer JavaScript dependencies in the browser.

Will you consider the option of not requiring Bloomreach JS in the browser for RSCs? I appreciate some JS is needed for Experience Manager when in preview mode, but it would be better for performance if we didn't need to include Bloomreach JS for our end users.

I am happy to help with the design of this.

@joerideg
Copy link
Contributor

Hi @cwmrowe ,
I think we are on the same page. Its what I intended to convey: We are looking to support RSC with the React SDK, which means we will write them as such as much as possible. For that we also need to convert our current component syntax, class components, to functional components. I dont see any reason why supporting RSC would not be possible considering we are really just rendering out a component hierarchy of static templates after a single fetch of the model from the backend. Your mapped components might or might not be a RSC, but React should probably pick that up automatically.

What we are still wondering about is how we would be able to expose the Page object to be used somewhere in the hierarchy, for example to render out the page title or something small that would not require a component. I'm sure we will find a way to do so with context without forcing the entire component tree to be client components.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants