perpetua.digital
Published on

Troubles setting up Adobe Target Prehiding in a NextJS app

Authors

The Adobe Target/Personalization Prehiding Snippet

I did not write the code for this blog. It uses the Tailwind Starter Blog Template which uses NextJS, React, and Tailwind CSS. 90% of the code I will never touch. I have, however, done some customization and deployed the Adobe Web SDK, amongst other things. Since I also use this site as a sandbox for proof-of-concept projects in Adobe Target, I have also setup Adobe Target prehiding. I need to make sure that content doesn't flicker and hinder the user experience for my 7 readers :)

I'm going to lay out how I have implemented prehiding on this site in the in hopes that it will help others get their own setup in order. My examples here use Adobe Web SDK prehiding setup, so the code will be a little different from what is used for AT.js, but the concepts should be the same.

As website building tools and javascript frameworks get more complicated, placing your prehiding code has become a bespoke process. Its even harder when you are only kind of a developer like me. Telling someone "Just put it at the top of the page" has sort of lost its meaning, as most pages these days are just almogomations of React components. What even is a page anymore, man? Anyway...

In the Web SDK specifically, its a challenge to have your render personalization in sync with your data layer eventing, what is happening on your UI, and what Target is returning.

If you are using the Adobe Web SDK you can find the Target prehiding snippet in the Web SDK extension:

prehide snippet location

I have yet to figure out what the Prehiding Style section above the code snippet section is for. I mean I get what its says it will do, but I have never gotten it to really have an effect on anything.

The code below is what you are copying out of the extension:

<script>
  !function(e,a,n,t){var i=e.head;if(i){
  if (a) return;
  var o=e.createElement("style");
  o.id="alloy-prehiding",o.innerText=n,i.appendChild(o),setTimeout(function(){o.parentNode&&o.parentNode.removeChild(o)},t)}}
  (document, document.location.href.indexOf("adobe_authoring_enabled") !== -1, ".personalization-container { opacity: 0 !important }", 3000);
</script>

At a high level, this code gives you a class called .personalization-container to place on elements that may be affected by Target/personalization efforts. This class will make elements with it are invisible (opacity 0) for 3 seconds or until a personalization response is sent back from the Adobe Edge Network.

I have slightly modified my code to use .prehide as the class name instead of .personalization-container because the word "personalization" feels too ambitious for my little blog here. You can see my prehide code in my repo under public > static > scripts > prehide.js my prehide code

Throw a little console log in there too just as a sanity check. I should probably make that a _satellite.log

I then load prehide.js in my main _app file which in turn loads the snippet on every page load and makes my .prehide class available. This method makes prehiding work for me, your experience may vary. By works for me, I mean that it prehides my elements tagged with the .prehide class until my Web SDK response returns with a personalization decision. That is exactly what the prehide snippet is supposed to do. However, this implementation method seems to go against everything the NextJS documentation says to do when working with external scripts. Here are the other ways I tried to incorporate this snippet before I came to this setup:

Methods that didn't work

React Component

First, I tried matching the original author of this template's structure of other analytics components and made a functional react component out of it.

import Script from 'next/script'

const Prehide = () => {
  return (
    <>
      <Script strategy="afterInteractive" id="adobe-prehide">
        {`
            console.log('begin prehide')
            !function(e,a,n,t){var i=e.head;if(i){
                if (a) return;
                var o=e.createElement("style");
                o.id="alloy-prehiding",o.innerText=n,i.appendChild(o),setTimeout(function(){o.parentNode&&o.parentNode.removeChild(o)},t)}}
                (document, document.location.href.indexOf("adobe_authoring_enabled") !== -1, ".prehide { opacity: 0 !important }", 3000);console.log("prehiding....")
        `}
      </Script>
    </>
  )
}

export default Prehide

This kinda worked, depending on where I tried to load it. Importing into the _app.tsx file with the other analytics components would yield prehiding on some tagged components, but seemed to be in a race with the hydration of the page elements.

Changing the script strategy had no effect. A prehidden tagged component would load, be shown, dissappear, then re-appear with test content from Target. Good behavior, in theory, but the timing was off. I tried importing it into the _document.tsx file, but that had no effect at all. I believe this is because code in _document is not read by the browser.

Inline Scripts

Next up was formatting the component using NextJS inline scripts formatting. I could not get the prehiding snippet to work at all with this formatting. I don't know why. I couldn't even get console.logs to work here either.

<Script id="adobe-prehide">
  {`!function(e,a,n,t){var i=e.head;if(i){
  if (a) return;
  var o=e.createElement("style");
  o.id="alloy-prehiding",o.innerText=n,i.appendChild(o),setTimeout(function(){o.parentNode&&o.parentNode.removeChild(o)},t)}}
  (document, document.location.href.indexOf("adobe_authoring_enabled") !== -1, ".prehide { opacity: 0 !important }", 3000);`}
</Script>

Using the dangerouslySetInnerHTML to set the prehide snippet in a next/script component also had no effect on prehiding elements. I don't know why ¯\_(ツ)_/¯

Prehiding in Action

I have a few elements on this site tagged for prehiding. I am using the strategy of tagging individual elements instead of entire section so as to not hinder my web vitals scores.

Subhead

This little subheadline on the homepage is gray by default, but I use Target to turn it green. The prehiding code prevents the default one from showing before Target can do its thing.

subhead default

subhead gif

Prehiding prevents the gray headline from flickering into the green one. (The shift is from the page reload)


About me image

avatar prehide gif

The profile image here has the prehiding class so you'll see it load slightly after the rest of the content. If I had a Target activity that altered that image, the activity's content would replace the hardcoded image seamlessly.

<Image
  src={avatar}
  alt="avatar"
  width="192px"
  height="192px"
  className="prehide h-48 w-48 rounded-full"
/>
prehide class on the image react component

What did I learn?

In the end, I got it to work, but I don't know exactly why. I guess what I learned here to keep trying until something works! If anyone has successfully integrated Target prehiding into a React/NextJS app such as mine, please reach out!

Sometimes when working on this stuff I feel like JK Simmons' character in this clip