Next.js with XM Cloud EDGE and GraphQL

 

Building a Component with Next.js and GraphQL from Sitecore XM Cloud Edge — No JSS Required

Goal:
In this blog post, we’ll walk through how to create a simple component in Next.js that fetches data from Sitecore XM Cloud Experience Edge using GraphQL — without using any JSS libraries.


🧩 What You’ll Need from Your Sitecore XM Cloud Instance

Before we start coding, we’ll need a few pieces of information from your Sitecore project:

1. Edge Token and GraphQL Endpoint

  • Log in to your Sitecore XM Cloud project.

  • Select your environment.

  • Go to the "Details" tab.

  • At the bottom, you’ll see Live GraphQL IDE (Experience Edge).

  • Click "Generate Delivery API Token" — this gives you the Edge API token and the GraphQL endpoint URL.



We’ll use this token later in our .env.local file.


🛠️ Project Setup: Next.js + GraphQL

1. Create a Next.js App

     Run the following command to bootstrap your app:


npx create-next-app@latest xmcloud



2. Install GraphQL Client

    We'll use graphql-request for making GraphQL queries.


npm install graphql-request




📁 Project Structure

3. Create a GraphQL Client

     Inside your project’s src folder:

  • Create a lib folder

  • Add a file named xmclient.ts (or any name you prefer)


// src/lib/xmclient.ts
import { GraphQLClient } from 'graphql-request';

const endpoint = process.env.NEXT_PUBLIC_XM_CLOUD_GRAPHQL_ENDPOINT || '';
const token = process.env.NEXT_PUBLIC_XM_CLOUD_EDGE_TOKEN || '';

export const client = new GraphQLClient(endpoint, {
headers: {
Authorization: `Bearer ${token}`,
  },
});



4. Add a Sitecore Link Helper

Create a helper folder in src, and add a file (e.g. sitecoreHelpers.ts) to parse Sitecore’s General Link field:

// src/helper/sitecoreHelpers.ts
export function parseGeneralLink(link: any) {
if (!link || !link.jsonValue) return { href: '', text: '' };


const { href, text } = link.jsonValue;
return {
    href,
text: text || href,
};
}





🔐 Environment Configuration

5. Create a .env.local File

Add the following environment variables:

NEXT_PUBLIC_XM_CLOUD_GRAPHQL_ENDPOINT=https://edge.sitecorecloud.io/api/graphql/v1

NEXT_PUBLIC_XM_CLOUD_EDGE_TOKEN=your-edge-token-here


🧱 Build the Component: HeroSection.tsx

6. Fetch and Render Data with GraphQL

Create a new component:

// HomepageContent.tsx
'use client';

import React, { useEffect, useState } from 'react';
import { xmGraphQLClient } from '@/lib/xmclient';
import { Field } from '@/interfaces/Field';
import { Item } from '@/interfaces/Item';
import { parseSitecoreLinkField } from '@/helper/sitecoreHelper';

const HEROCOMPONENT_QUERY = `
  query heropcomponnetdata {
    item(path: "/sitecore/content/demo-sites/kgcdemo/Data/Heroitem", language: "en") {
      fields {
        name
        value
      }
    }
  }
`;

interface HeropComponentDataResponse {
  item: Item;
}

const HeroSection = () => {
  const [fields, setFields] = useState<Field[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    xmGraphQLClient
      .request<HeropComponentDataResponse>(HEROCOMPONENT_QUERY)
      .then((res) => {
        setFields(res.item.fields || []);
        setLoading(false);
        console.log(res);
      })
      .catch((err: Error) => {
        setError(err.message);
        setLoading(false);
        console.error("Error fetching data:", err.message);
      });
  }, []);

  // Convert fields array to an object with field names as keys
  const fieldsObj = React.useMemo(() => {
    const obj: Record<string, string> = {};
    fields.forEach(f => {
      obj[f.name] = f.value;
    });
    return obj;
  }, [fields]);

  // Parse Sitecore link fields
  const resumeLink = parseSitecoreLinkField(fieldsObj.ResumeApplication, 'Resume Application');
  const loginViewLink = parseSitecoreLinkField(fieldsObj.LoginView, 'Login to view');
  const consolidationLink = parseSitecoreLinkField(fieldsObj.ConsolidationLink, 'Apply online');

  const resumeUrl = resumeLink.url;
  const loginViewUrl = loginViewLink.url;
  const consolidationUrl = consolidationLink.url;

  if (loading) return <section>Loading...</section>;
  if (error) return <section>Error: {error}</section>;
  if (!fields || fields.length === 0) return <section>No data found.</section>;

  return (
    <section className="flex flex-col md:flex-row w-full min-h-[400px]">
      {/* Left: Background image with overlay and content */}
      <div
        className="relative flex-1 flex items-center justify-center min-h-[400px] bg-cover bg-center"
        style={{ backgroundImage: "url('/DA/banner.jpg')" }}
      >
        <div className="absolute inset-0 bg-black/40" />
        <div className="relative z-10 p-8 md:p-16">
          <div className="mb-6">
            <h2 className="text-white text-xl font-bold mb-1">
              Debit card <span className="text-red-500">&#8226;</span>
            </h2>
            <h2 className="text-white text-xl font-bold">
              Credit Card <span className="text-red-500">&#8226;</span>
            </h2>
          </div>
          <h1 className="text-white text-4xl md:text-5xl font-extrabold mb-8 leading-tight max-w-xl">
            {fieldsObj.SubTitle} <span className="text-[#ff4b5c]"></span>
          </h1>
          <div className="flex flex-col gap-2 max-w-xs">
            <a
              href="#"
              className="bg-teal-500 hover:bg-teal-600 text-white font-semibold py-3 rounded mb-2 text-center transition"
            >
              {fieldsObj.ApplyOnline}
            </a>
            <a href={resumeUrl} className="text-white underline text-center">
              {resumeLink.text || fieldsObj.ResumeApplication}
            </a>
          </div>
        </div>
      </div>

      {/* Right: Info cards */}
      <div className="flex flex-col flex-shrink-0 w-full md:w-[420px]">
        <div className="flex-1 bg-[#17695b] p-8 flex flex-col justify-center min-h-[200px]">
          <h3 className="text-white text-2xl font-bold mb-2">{fieldsObj.PulseTitle}</h3>
          <p className="text-white text-lg mb-6">{fieldsObj.PulseDescription}</p>
          <a
            href={loginViewUrl}
            className="text-white text-right font-semibold flex items-center gap-2 hover:underline"
          >
            {loginViewLink.text} <span>&rarr;</span>
          </a>
        </div>
        <div className="flex-1 bg-[#20544a] p-8 flex flex-col justify-center min-h-[200px]">
          <h3 className="text-white text-2xl font-bold mb-2">{fieldsObj.ConsolidationTitle}</h3>
          <p className="text-white text-lg mb-6">{fieldsObj.ConsolidationDescription}</p>
          <a
            href={consolidationUrl}
            className="text-white text-right font-semibold flex items-center gap-2 hover:underline"
          >
            {consolidationLink.text} <span>&rarr;</span>
          </a>
        </div>
      </div>
    </section>
  );
};

export default HeroSection;



🌐 Live Demo and GitHub Repository

The application is deployed on Vercel. You can view it live here:
Homepage
About Us
Code is available on GitHub:

👉 GitHub Repo


✅ Summary

    In this blog post, you’ve learned how to:

  • Get data from Sitecore XM Cloud Edge using GraphQL

  • Set up a Next.js project to use the graphql-request client

  • Parse Sitecore General Link fields

  • Render that data inside a Next.JS component

        All without using JSS libraries!

Comments

Popular posts from this blog

Solrcloud With Zookeeper -Single server setup

Render Sitecore Experience Forms Using Sitecore XP 10.4 with a Headless Approach (Next.js + JSS SDK)