Guides & Tutorials

How to Personalize a Next.js E-Commerce Site with Edge Functions and Builder.io

In eCommerce, finding the right balance between performance and personalization can feel like a difficult tradeoff. On the one hand, buyers expect a performant website, and bounce rates increase with every millisecond of latency. On the other, a personalized shopping experience creates higher conversion rates. Personalizing your ecommerce site allows for:

  • Displaying offers to users based on their geolocation
  • Authenticating requests and gating content
  • Showing products or collections to different audiences—for example, showcasing women’s clothes to users who tend to shop in that category
  • Showing different content to new vs return visitors

The most common workarounds are to use a hybrid framework that supports both statically and dynamically rendered pages, like Next.js. From there, you can build a primarily static site and fall back to server-side rendering (SSR) for pages that need to be personalized, or to simply allow client-side rendering (CSR) to render personalized page sections (like the sign in/sign out links in a header). Both of these approaches are a lot slower and lead to suboptimal user experience. How do you get the best of both worlds? With Edge Functions.

With Netlify Edge Functions, you can execute arbitrary code at Netlify’s network edge. You create a Next.js middleware capable of geolocation, authentication, audience identification, or any other operation that needs to be executed at request time. Once it’s done, your middleware modifies the request or the response, performing just enough work to personalize statically-generated content.

Edge Functions’ middleware-based approach is much faster than traditional SSR because it doesn’t generate an entire page for each request. It runs in front of static pages or pages served with Incremental Static Regeneration (ISR) by the Netlify CDN, letting you sprinkle some dynamic magic onto your pre-rendered pages, all at the edge, reducing latency.

We’ve provided a tutorial below to give you a taste of how powerful Edge Functions can be, especially when combined with a performant content management system (CMS).

And that’s where Builder.io comes in.

Builder is a visual CMS that enables your teams to build pages quickly and manage content using your existing tech stack and Builder’s drag-and-drop Visual Editor. With additional built-in A/B testing and targeting features, it becomes even easier to create and deliver personalized content to the right audiences.

In this tutorial, you’ll learn how to use Netlify and Builder to:

  • Create and deploy a Next.js app onto Netlify.
  • Visually create two product collection pages to target two different audiences. For this tutorial, we’ll use clothing shoppers and jewelry shoppers.
  • Personalize your statically-generated Next.js pages using Builder and Next.js middleware running on Netlify Edge Functions.
  • Implement Builder best practices so that non-developers on your team can easily add to your site and personalize the shopping experience

What you’ll need to create your personalized eCommerce site

If you don’t have them already, create an account with Netlify and one with Builder.

Netlify has a free tier option that has all the features you’ll need to deploy your site.

For Builder, you’ll need to sign up for a free trial, which includes the Growth plan by default. The Growth plan and higher-level plans support custom targeting attributes, a feature we talk about in this tutorial.

Step 1: Create your Next.js App

Create a Next.js app if you don’t already have one started - for instance using this Next.js e-commerce starter. Make sure that you have Node.js installed (as of writing this, at least version 12.22.0) and run the following command:

npx create-next-app@latest

Follow the guided prompts to complete setup. For this tutorial, we’ll use JavaScript rather than TypeScript, and we’ll name our app netlify-builder-edge-demo.

Once you’ve finished creating your app, change into your new project directory:

cd netlify-builder-edge-demo

Next, install Builder’s dependencies:

npm install @builder.io/react @builder.io/personalization-utils

@builder.io/react is Builder’s React SDK. With it, you can fetch the pages that you design within Builder’s Visual Editor and render them within your Next.js page templates. @builder.io/personalization-utils is a lightweight utility library that makes it easier to write the necessary middleware. Both are fully open source.

Install Netlify’s development dependencies:

npm install -D @netlify/build @netlify/functions @netlify/plugin-nextjs

Add the BUILDER_PUBLIC_KEY environment variable to next.config.js:

// next.config.js

const nextConfig = {
  ...,
  env: {
    BUILDER_PUBLIC_KEY: process.env.BUILDER_PUBLIC_KEY
  }
}

Create your Netlify configuration file netlify.toml:

# netlify.toml

[build]
command = "next build"
base = "."
publish = ".next"

[build.environment]
NEXT_USE_NETLIFY_EDGE = "true"

[[plugins]]
package = "@netlify/plugin-nextjs"

You’re now ready to start your development server with the following command. Replace YOUR_API_KEY with your Builder account’s Public API Key, which you can find under Account Settings:

BUILDER_API_KEY=YOUR_API_KEY npm run dev

Step 2: Integrate your Builder page model

Before you can create drag-and-drop pages in Builder, you need to integrate Builder with your site.

That’s because when designing a page within the Visual Editor, Builder shows a live preview of your content embedded within your website’s page template along with all of your site’s CSS and any content on that page that’s not managed by Builder. Integration is the magic that makes the live preview work.

To get started, log into your Builder account and click on Models in the left nav. Click Add/Edit Models and then click on the Page model.

On the Model Options page, edit the Preview URL to http://localhost:3000. Builder’s Visual Editor will now render your content within your Next.js index page while editing.

Select Builder.io model option

Click Save when you’re done.

Now let’s write the middleware your app needs to get your personalization data.

Step 3: Create middleware

Next.js supports middleware, which are functions that can arbitrarily rewrite user requests to your site.

Copy and paste the middleware code below into a new file called pages/_middleware.js:

// pages/_middleware.js

import { NextResponse } from "next/server";
import {
  PersonalizedURL,
  getUserAttributes
} from "@builder.io/personalization-utils";

export default function middleware(request) {
  const url = request.nextUrl;
  const requestPath = url.pathname;

  // Rewrite any URL that has a path that doesn't start with /builder,
  // which is special (see below).
  if (!requestPath.startsWith("/builder")) {
    const query = Object.fromEntries(url.searchParams);
    // Create a new URL with a base64 hash in the query params.
    const personlizedURL = new PersonalizedURL({
      pathname: requestPath,
      attributes: {
        // Include any builder.userAttributes.* query params.
        ...getUserAttributes({ ...query }),
        // Include the visitor's audience 
        audience: query.audience || "clothing",
      },
    });

    // Rewrite the URL path with the hash.
    url.pathname = personlizedURL.rewritePath();

    // After returning the rewritten URL, the browser silently redirects
    // to /builder/the-base64-hash, which is our page template from before.
    // The URL in the browser's address bar doesn't change.
    return NextResponse.rewrite(url);
  }

  return NextResponse.next();
}

The example above does the following:

  • Extracts audience data from query parameters, falling back to clothing. You can get this information from any source you want, like cookies, but we’ll stick to query parameters to keep this tutorial simple.
  • Extracts any builder.userAttributes.* query parameters. These are important for Builder editing features like Builder Studio.
  • Rewrites the request URL to point to /builder/[hash], where [hash] contains the audience and builder.userAttributes.* query parameters encoded in Base64.

Returning the rewritten URL triggers a silent redirect to the page created previously in this tutorial, which decodes the audience and uses it to target the appropriate Builder page.

From the user’s perspective, the address bar URL doesn’t change and there’s no sign of a redirect. It just works.

Next, you can create the Next.js page template that will fetch and render your personalized Builder page.

Step 4: Create your Next.js page template

Builder’s SDK renders pages that you create in Builder within your framework’s page templates.

Create a Next.js page in your project directory at pages/builder/[hash].js with the following contents:

import { BuilderComponent, builder } from "@builder.io/react";
import { PersonalizedURL } from "@builder.io/personalization-utils";
import { useEffect } from "react";

builder.init(process.env.BUILDER_API_KEY);

export async function getStaticProps({ params }) {
  const personlizedURL = PersonalizedURL.fromHash(params.hash);
  const attributes = personlizedURL.attributes;
  const page = await builder
    .get("page", {
      userAttributes: attributes,
    })
    .promise();

  return {
    props: {
      page: page || null,
      attributes,
    },
  };
}

export default function Path({ page, attributes, locale }) {
  useEffect(() => {
    builder.setUserAttributes(attributes);
  }, []);

  return (
    {% raw %}
    {/* Your header goes here. */}
    <YourHeader />
    <BuilderComponent
      context={{ attributes }}
      data={{ attributes, locale }}
      model="page"
      content={page}
    />
    {/* Add the rest of your page here. */}
    <RestOfYourPage />
    {% endraw %}
  );
}

<BuilderComponent> renders your Builder page, which is fetched by builder.get within getStaticProps. This is the basic flow for integrating Builder with any Next.js app.

The example also adds some details for personalizing your page:

const personlizedURL = PersonalizedURL.fromHash(params.hash);
const attributes = personlizedURL.attributes;
const page = await builder
  .get("page", {
    userAttributes: attributes,
  })
  .promise();

The page passes the personalization data, received from a query parameter from the middleware created in the next step, to Builder as custom targeting attributes. Builder uses these targeting attributes to pick which page to load.

In order to make targeting work, we’ll set up our custom targeting attributes next.

Step 5: Set up custom targeting attributes

From within your Builder dashboard, click Account Settings in the left nav.

Builder.io Account Settings)

Click on the edit button for Custom targeting attributes.

Builder.io custom targeting attributes

Create a new custom targeting attribute named “audience” with a String type. Click Enum and add two values: “clothing” and “jewelry”, for our clothing shoppers and jewelry shoppers, respectively.

Select eCommerce personas for Builder.io

Click Save when you’re done.

You can use your new custom targeting attribute when creating personalized pages.

Step 6: Create two Builder pages

The idea is to use custom targeting attributes to create two pages, with each page personalized to the user based on their shopping preferences. In this case, we’re personalizing content based on whether they shop most often for clothing or jewelry.

Create two Builder Pages for different attributes

To get started, click on Content in the Builder dashboard’s left nav. Once you’re on the Content page, click +New.

Add new pages in Builder.io

When prompted, name your page “Clothing”, set the URL to / and click Create Page.

image

Now comes the fun part: creating a clothing page for your store!

Use the Visual Editor to drag and drop image and text blocks onto the editing area to compose your page. Once you’ve got some content up, click the Edit Targeting button.

Target specific eCommerce audience with visual editor

Within the Targeting modal, click +Target. Add a new Audience target and set “clothing”.

Target one specific eCommerce audience with visual editor in Builder.io

Great! Your page will only appear when you pass “clothing” as a targeting attribute. Click Publish in the upper right corner of the Visual Editor when you’re done.

Now create a page for the jewelry shoppers. Follow the same steps as above for creating your clothing page, and pay close attention to the following details:

  • Name the page “Jewelry”.
  • As before, set the URL to /.
  • Add an Audience target with a value of “jewelry”.
  • Don’t forget to click Publish when you’re done!

Here’s an example of what your jewelry page might look like:

Sample eCommerce page with audience targeting

Step 7: Test it locally

Now that you’ve created your personalized pages, it’s time to test it out in your development environment.

Navigate to http://localhost:3000 in your browser. You should see your clothing page:

image

Why the clothing page? Let’s follow the request:

  • An inbound request for / reaches your site. There’s no index page for that path, but the middleware intercepts it.
  • The middleware looks for an audience value from the URL’s search string. Not finding any, it defaults to clothing, encodes this data, and silently redirects to /builder/[hash] where [hash] is an encoded version of the audience value.
  • The Next.js page template for /builder/[hash] decodes the hash and fetches your Builder page using clothing as the value for the audience targeting attribute and / as the path.
  • Builder finds two pages for /, but only one of them has clothing as its audience targeting attribute, which it returns.
  • Next.js renders the Builder page within the page template and responds with the fully-rendered page.

Now let’s see what happens when we navigate to http://localhost:3000/?audience=jewelry:

image

It displays the jewelry page as expected. Huzzah!

And for the sake of completeness, let’s try explicitly setting the audience to “clothing” by going to http://localhost:3000/?audience=clothing:

Clothing page image

The browser shows the clothing page as expected.

Step 8: Deploy to Netlify

Now that you’ve confirmed everything’s working on localhost, deploy to Netlify where everyone can see your hard work.

Netlify provides several ways to deploy a site. Our recommendation is to upload your site’s code to a git repository using one of their supported providers, then import your site from the repo.

Step 9: Change the preview URL

Remember when you set the preview URL for your page model to http://localhost:3000?

It’s standard practice after publishing your site to change that development URL to your production URL. That way, your teammates can edit pages without having to spin up a local development environment.

Click Models in the left nav within the Builder dashboard and click Page.

On the Model Options page, enter your published site’s URL as the preview URL:

image

Click Save when you’re done.

Conclusion

Congratulations! You’ve successfully created a super-performant and personalized site using Next.js SSG and Netlify Edge Functions. And it’s integrated with Builder.io, so you can add more pages and content with ease.

You can continue adding to your site, using Builder’s targeting feature to create new content for different segments of your user base. Or use your newfound knowledge to integrate Builder with your current codebase and deploy to Netlify.

Keep reading

Recent posts

How do the best dev and marketing teams work together?