Time flies, I published my static site generator for Sitecore JSS back in August. Most of the questions I got around it involved personalization, which were perfectly valid! Anastasiya Flynn gave me the suggestion that I could use fetchPlaceholderData client side and make call backs to origin for personalized data for a particular placeholders:

There's just not much information about this call online though. (EDIT: Lies, there's some info here) So let's take a look at it. Just to be clear, you should only really use this service if you have a valid use case. If you're doing normal Sitecore JSS development with layout service with no caching, then you do not need this. The use case is basically "I've cached all my layout service data" or "I've used a static site generator on my JSS site but still want sections personalized client-side."

So let's start in the Sitecore JSS GitHub repository. This function lives in dataApi in the sitecore-jss NPM package. All of the framework specific frameworks already re-export this dataApi class. So if you're using React, the code to import this class is:

import { dataApi } from '@sitecore-jss/sitecore-jss-react';

If you're looking for a existing example of using dataApi, look no further than your RouteHandler.js in your Sitecore JSS application, or check it out here.

Okay so we've got our dataApi imported now and we can look at what's available on it. There's already a function we can call to fetch just the content for a particular placeholder. That function is defined here. If you look around the JSS repository some, you can see that this function isn't really used...

via GIPHY

Let's use it. We can define a function in our code that looks a little something like this:

import { dataApi } from '@sitecore-jss/sitecore-jss-react';
import { dataFetcher } from './dataFetcher';
import config from './temp/config';

// in our React.Component
fetchPlaceholder(placeholderName, itemLanguage, itemId) {
    return dataApi.fetchPlaceholderData(placeholderName, itemId, {
        layoutServiceConfig: { host: config.sitecoreApiHost },
        querystringParams: { sc_lang: itemLanguage, sc_apikey: config.sitecoreApiKey },
        fetcher: dataFetcher,
    });
}

config is the core configuration file for our JSS application. It tells use which endpoints to hit and has our API keys configured in it. dataFetcher exists in all of the sample applications and is basically a shim around whatever fetch-style library you're using to make layout service requests already.

fetchPlaceholderData generates a GET request and the URL looks something like this: http://adamlamarredevtestsvelte.eastus.cloudapp.azure.com/sitecore/api/layout/placeholder/jss?item=/&sc_lang=en&sc_apikey={06FE0175-FE3E-4A37-877F-2F5B29DF57A2}&placeholderName=jss-main

The response JSON is the layout service data for the placeholder that is being requested. In this example, we're requesting jss-main.

{
    "name": null,
    "path": "jss-main",
    "elements": [{
        "uid": "2c4a53cc-9da8-5f51-9d79-6ee2fc671b2d",
        "componentName": "ContentBlock",
        "dataSource": "{E681A5EB-7CF6-5A53-A1C0-FAD03D7C1E62}",
        "fields": {
            "heading": {
                "value": "Welcome to Sitecore JSS!"
            },
            "content": {
                "value": "<p>This is a content body content field, it's great content.</p>"
            }
        }
    }]
}

So now we can take the elements field and re-render our Placeholder using it. It might the situation where we render a blank Placeholder to start out and then use this data to do the initial render, or we could use the data we might already have from layout service and just swap it out with this new data and re-render the Placeholder. The choice is yours.

Code?

Yeah, I have code for your consideration. Not going to just tease you like the last time with my Cloudflare Workers post.

Save this file wherever you want but I placed it in /src/ClientSidePlaceholder.js. Feel free to rename as well, I'm kind of meh on it but I thought this was close enough.

After that you can just replace existing placeholders with this component wherever you want this functionality, ie:

import ClientSidePlaceholder from './ClientSidePlaceholder';

// in render() or in your component function
<ClientSidePlaceholder name="jss-placeholder-name" rendering={route} hideInitialContents={true} />

Leave off hideInitialContents if you want to use the layout service data for the initial render.

Conclusion, should you use this?

I think this is neat functionality and should probably be built into the Sitecore JSS placeholder (the service already exists!) and maybe I'll throw a PR in eventually if the feedback is generally positive for this functionality.

This component should be used sparingly and it means you must ensure your origin server is online and available to service these callbacks. Using this component on a placeholder such as jss-main will really defeat the purpose of having your site statically rendered. I think this is a great candidate for areas you know you will want personalization, such as a sidebar or similar.

There's more to come here. I publish this because I think it's a valid use case but I see it evolving further.