Next.js Integration for SEO with SSR¶
NXTBN E-commerce GraphQL API Integration Guide¶
Table of Contents¶
Overview¶
This guide demonstrates how to integrate the NXTBN E-commerce GraphQL API with Next.js using server-side rendering (SSR) for optimal SEO performance. We’ll use getServerSideProps
and getStaticProps
where appropriate to ensure content is rendered on the server.
Setup¶
First, set up your GraphQL client configuration:
// lib/graphql-client.ts
import { GraphQLClient } from 'graphql-request';
const endpoint = process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT || 'http://127.0.0.1:8000/graphql/';
export const graphqlClient = new GraphQLClient(endpoint);
Product Pages¶
Single Product Page¶
Create a dynamic product page with SSR:
// pages/products/[slug].tsx
import { GetServerSideProps } from 'next';
import { graphqlClient } from '@/lib/graphql-client';
const PRODUCT_QUERY = `
query GetProduct($id: ID!) {
product(id: $id) {
id
name
summary
description
slug
thumbnail
metaTitle
metaDescription
defaultVariant {
id
name
price
}
}
}
`;
export const getServerSideProps: GetServerSideProps = async ({ params }) => {
try {
const { product } = await graphqlClient.request(PRODUCT_QUERY, {
id: params?.slug
});
return {
props: {
product,
seo: {
title: product.metaTitle || product.name,
description: product.metaDescription || product.summary,
openGraph: {
images: [{ url: product.thumbnail }]
}
}
}
};
} catch (error) {
return { notFound: true };
}
};
const ProductPage = ({ product, seo }) => {
// Your product page component code
};
export default ProductPage;
Product Listing Page¶
Implement a paginated product listing with SSR:
// pages/products/index.tsx
import { GetServerSideProps } from 'next';
const PRODUCTS_QUERY = `
query GetProducts($first: Int!, $after: String) {
allProducts(first: $first, after: $after) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
id
name
summary
thumbnail
price
}
}
}
}
`;
export const getServerSideProps: GetServerSideProps = async ({ query }) => {
const { page = 1 } = query;
const first = 12; // Products per page
try {
const { allProducts } = await graphqlClient.request(PRODUCTS_QUERY, {
first,
after: ((page as number) - 1) * first || null
});
return {
props: {
products: allProducts.edges.map(({ node }) => node),
pagination: {
hasMore: allProducts.pageInfo.hasNextPage,
endCursor: allProducts.pageInfo.endCursor
}
}
};
} catch (error) {
return { notFound: true };
}
};
Category Pages¶
Implement category pages with static paths and server-side props:
// pages/categories/[slug].tsx
import { GetStaticPaths, GetStaticProps } from 'next';
const CATEGORIES_QUERY = `
query GetCategories {
categoriesHierarchical {
edges {
node {
id
name
slug
}
}
}
}
`;
const CATEGORY_PRODUCTS_QUERY = `
query GetCategoryProducts($categoryName: String!, $first: Int!) {
allProducts(categoryName_Icontains: $categoryName, first: $first) {
edges {
node {
id
name
summary
thumbnail
}
}
}
}
`;
export const getStaticPaths: GetStaticPaths = async () => {
const { categoriesHierarchical } = await graphqlClient.request(CATEGORIES_QUERY);
const paths = categoriesHierarchical.edges.map(({ node }) => ({
params: { slug: node.slug }
}));
return {
paths,
fallback: 'blocking'
};
};
export const getStaticProps: GetStaticProps = async ({ params }) => {
try {
const { allProducts } = await graphqlClient.request(CATEGORY_PRODUCTS_QUERY, {
categoryName: params?.slug,
first: 12
});
return {
props: {
products: allProducts.edges.map(({ node }) => node)
},
revalidate: 60 // Revalidate every minute
};
} catch (error) {
return { notFound: true };
}
};
Search Implementation¶
Create a server-side search implementation:
// pages/search.tsx
import { GetServerSideProps } from 'next';
const SEARCH_QUERY = `
query SearchProducts($search: String!, $first: Int!) {
allProducts(search: $search, first: $first) {
edges {
node {
id
name
summary
thumbnail
price
}
}
}
}
`;
export const getServerSideProps: GetServerSideProps = async ({ query }) => {
const { q: searchQuery, page = 1 } = query;
const first = 12;
if (!searchQuery) {
return {
props: {
products: [],
searchQuery: ''
}
};
}
try {
const { allProducts } = await graphqlClient.request(SEARCH_QUERY, {
search: searchQuery,
first
});
return {
props: {
products: allProducts.edges.map(({ node }) => node),
searchQuery
}
};
} catch (error) {
return { props: { products: [], searchQuery, error: 'Search failed' } };
}
};
SEO Optimization¶
Implement SEO components for all pages:
// components/SEO.tsx
import Head from 'next/head';
interface SEOProps {
title: string;
description: string;
image?: string;
type?: string;
}
export const SEO = ({ title, description, image, type = 'website' }: SEOProps) => (
<Head>
<title>{title}</title>
<meta name="description" content={description} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
{image && <meta property="og:image" content={image} />}
<meta property="og:type" content={type} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
{image && <meta name="twitter:image" content={image} />}
</Head>
);
Best Practices for SEO¶
Dynamic Metadata: Always use the product’s or category’s
metaTitle
andmetaDescription
when available.
// Example usage in product page
<SEO
title={product.metaTitle || product.name}
description={product.metaDescription || product.summary}
image={product.thumbnail}
type="product"
/>
Structured Data: Implement JSON-LD for products:
// components/ProductStructuredData.tsx
const ProductStructuredData = ({ product }) => (
<Head>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'Product',
name: product.name,
description: product.description,
image: product.thumbnail,
offers: {
'@type': 'Offer',
price: product.defaultVariant.priceRaw,
priceCurrency: 'USD', // Update according to your currency
availability: 'https://schema.org/InStock'
}
})
}}
/>
</Head>
);
Performance Optimization¶
Use
Image
component fromnext/image
for optimized images.Implement proper caching strategies.
Use incremental static regeneration where appropriate.
// Example of image optimization
import Image from 'next/image';
const ProductImage = ({ product }) => (
<Image
src={product.thumbnail}
alt={product.name}
width={500}
height={500}
priority={true}
className="product-image"
/>
);
Additional Recommendations¶
Use semantic HTML elements.
Implement proper error boundaries.
Add loading states for better UX.
Implement proper TypeScript interfaces for all data structures.
Use proper HTTP status codes for error handling.
Implement proper caching strategies using Next.js built-in features.
This integration ensures optimal SEO performance by:
Server-side rendering of all critical pages.
Proper metadata and structured data implementation.
Optimized image loading.
Clean URLs and proper routing.
Fast page loads through efficient data fetching.