---
title: "Moving the web version to Next.js for SEO"
description: "We rebuilt Hargapedia's website in Next.js after finding that the original React single-page app wasn't being indexed properly by Google."
url: "https://rayson.me/writing/hargapedia-web-seo/"
category: "Engineering"
author: "Janarayson"
published: 2021-05-18
---

Last July, we launched [Hargapedia's first proper website](/writing/hargapedia-web/), where people could browse our products and compare online prices without installing the mobile app.

We built it as a React single-page application. It worked well enough when someone visited the site directly, but there was a problem we hadn't solved: Google wasn't indexing the pages properly.

The server was returning a mostly empty HTML page with JavaScript that would load the data and build the interface in the browser. That was fine for people, but it made things less straightforward for search engines.

Google [can execute JavaScript](https://developers.google.com/search/docs/crawling-indexing/javascript/javascript-seo-basics), but rendering those pages requires additional processing. On top of that, our product URLs used IDs rather than readable names, and the pages didn't have enough useful text or metadata.

For a price comparison website, that's a fairly important problem. Someone searching Google for the price of a particular product should be able to find its page on Hargapedia.

So in February we started rebuilding the website on Next.js. It's been live for a few weeks now.

## Rebuilding the pages

The main change was server-side rendering.

With the original React app, the browser had to run JavaScript and request the data before the product page appeared. With Next.js, the server renders the page and sends back HTML containing the product name, prices and other content.

That gives search engines something useful to read without having to build the entire page in a browser first.

I worked on most of the frontend during the first week of February, rebuilding the header, category menu, search, product cards, price components, modals and footer.

A colleague connected the pages to our API over the following weeks.

We kept the serverless web API we'd built the previous year. It still sits between the website and Hargapedia's main GraphQL API, returning the data the website needs. We extended it with additional endpoints for the new URLs.

That meant we could replace the frontend without having to rebuild the entire backend again.

But rendering the pages on the server was only part of the work.

## Giving the pages proper addresses

The old website used IDs in its URLs. You could open a product page, but its address didn't tell you much about the product.

With the Next.js rebuild, products, categories, brands, specials and vouchers now have their own slugs.

Instead of an address based on a database ID, a product's URL uses its name. The same applies to the other types of pages.

Slugs matter for SEO. A URL that contains the product name is another signal to Google about what the page is, and it reads better when it shows up in search results.

We also added redirects from the old addresses to the new ones, so links people had already shared would continue working.

Then came the sitemap.

The first version listed our categories and most-searched products. This month, we've expanded it to include every product.

To do that, we added an endpoint to the web API that returns all the product slugs. The sitemap uses those to generate the URLs, and the site's robots file tells search engines where to find it.

With more than just a handful of pages to deal with, maintaining that list manually wouldn't have made much sense.

## Giving Google something to read

The pages also needed more information than they had before.

We added proper page titles, descriptions and Open Graph metadata using [next-seo](https://github.com/garmeeh/next-seo), so each page has information relevant to its content rather than relying on generic site-wide values.

Some pages needed actual text too.

The old category and brand pages were mostly grids of product cards. They were useful if you were already browsing the website, but didn't say much about what was on the page.

Category pages now have descriptions of the products they contain, while brand pages have introductions.

For pages that follow a common structure, we generate the text from templates. That lets us provide descriptions across a large number of pages without having to write every one individually.

We also spent some time cleaning up the frontend, stripping console logs from production builds and optimising images in April.

There was one more change involving the images themselves.

## Moving the watermarks into image processing

Since the original website launched, we've been displaying watermarked product images on the web.

The mobile app uses the original images, while the website gets versions with the Hargapedia watermark across them.

Initially, we generated those watermarked versions on demand using [imgproxy](https://imgproxy.net). The API returned a signed URL containing the image transformation instructions, and the proxy applied the watermark whenever the image was requested.

That worked, but the URLs were long strings of transformation parameters and signatures.

This month, we changed how those images are produced.

Now, when a product image is saved, the API publishes a message. A worker picks it up, resizes the image, applies the watermark and stores the processed files under the product.

The filenames use the product's slug, so the image URL identifies the product rather than being a long signed proxy address.

<Figure
  src={midea}
  alt="A product photo of a black Midea microwave oven with the word Hargapedia in large grey letters across the middle of the image"
  caption="A watermarked product image as it appears on the website."
  small
/>

Google had started picking up our product images as it indexed the website, so the image addresses were another part of what we wanted to improve.

The result is a website that still uses the same Hargapedia products and API, but serves its pages differently. The content is rendered on the server, products have readable URLs, and the site has the metadata and sitemap needed for search engines to discover its pages.

It's still the same price comparison website we launched last year. This time, we're making it easier for people to find those prices through Google in the first place.
