semi programmatic seo
author image

David Dumont

04/10/2022

Semi programmatic SEO example

Why semi-programmatic SEO ?

Programmatic SEO is an excellent tool for transforming large amounts of data into web pages that respond perfectly to a user's specific search queries. With programmatic SEO, it is possible to focus on very specific keywords and significantly increase traffic to your site.

However, while the page construction and integration can be accelerated, one may need to add unique content to the page. This will improve SEO and differentiates the pages from each other, allowing to reach a better ranking.

Thus, semi-programmatic SEO provides the best of both worlds: accelerated page construction (and update), and better page customization through unique content.

Example : hotel comparison

Let's say we want to compare N hotels in the same city. If we make multiple pages comparing each time two hotels, the number of pages can quickly be very high. For example, if we want to compare 10 hotels two by two, we will need 45 pages!

In the following example, we compare only 3 hotels: hotel A, hotel B and hotel C.

So we have to build the pages :

  • hotel A vs hotel B

  • hotel A vs hotel C

  • hotel B vs hotel C

Data

For simplicity, we have incorporated the hotel data into a data.ts file :

interface SchemaHotelsTable {
    slug: string
    hotelName: string
    url: string
    overall_rating: string
    service_rating:string
}

export const hotels: SchemaHotelsTable[] = [
{
    slug: 'hotelA',
    hotelName: 'Hotel A',
    url: 'https://hotelA.com',
    overall_rating: '4',
    service_rating:'2',
},
{
    slug: 'hotelB',
    hotelName: 'Hotel B',
    url: 'https://hotelB.com',
    overall_rating: '5',
    service_rating:'4',
    },
    {
    slug: 'hotelC',
    hotelName: 'Hotel C',
    url: 'https://hotelB.com',
    overall_rating: '2',
    service_rating:'1',
    },
]

Building a reusable component

We now want to reuse a Suncel composite that will integrate all the hotel data to be displayed.

import React, { Fragment } from 'react'
import { SuncelBlock } from '@SuncelIO/suncel-nextjs-package'
import {hotels as hotelData} from './data'  //hotel data import

type HotelCompareProps = {
  hotels: string[]
}

export const HotelCompare: SuncelBlock<HotelCompareProps> = ({ hotels=[]}) => {
  
  // keep only selected hotels
  const filteredHotels = hotelData.filter(hotel =>
    hotels?.includes(hotel.slug)
  )

  //display hotel cards
  return (
    <div>
      <div className="py-6 mx-auto bg-white">
        <div className="flex flex-col flex-wrap justify-center mx-auto md:flex-row">
          {filteredHotels.map((hotel, hotelIdx) => (
            <section key={hotel.hotelName} className='max-w-md p-4 mx-8 border-2 border-gray-100 rounded-lg shadow-lg'>
              <div className="flex flex-col justify-center px-4 mb-8">
                <h2 className="mt-6 text-3xl font-medium leading-6 text-center text-gray-800 ">
                  {hotel.hotelName}
                </h2>

                  <div className="flex justify-center">
                    <div className="flex items-center justify-center w-full h-full text-center md:text-left">
                      <a
                        href={hotel.url}
                        className="w-full p-2 mt-4 text-sm font-bold text-center text-white bg-blue-900 hover:bg-insurly-darker-blue rounded-xl hover:scale-105">
                        Visit Hotel
                      </a>
                    </div>
                  </div>
              </div>

                <table className="w-full">
                  <caption
                    className="px-4 py-1 text-sm font-medium text-left text-gray-800 border-t border-gray-200 bg-blue-50"
                    style={{ captionSide: 'top' }}>
                  </caption>
                  <tbody className="divide-y divide-gray-200">
                      <tr  className='border-t border-gray-200'>
                        <th className="px-4 py-5 text-sm font-normal text-left text-gray-500">
                          Global Rating
                        </th>
                        <td className="py-5 pr-4">
                         {hotel.overall_rating}
                        </td>
                      </tr>

                      <tr  className='border-t border-gray-200'>
                        <th className="px-4 py-5 text-sm font-normal text-left text-gray-500">
                          Service Rating
                        </th>
                        <td className="py-5 pr-4">
                         {hotel.service_rating}
                        </td>
                      </tr>
                  </tbody>
                </table>
              
            </section>
          ))}
        </div>

      </div>
    </div>
  )
}


HotelCompare.suncel = {
  slug: 'hotelCompare',
  displayName: 'Display Hotel Comparison',
  defaultProps: {},
  editor: {
    settings: [
        // select multiple hotels to display
        {
            type:'select',
            slug:'hotels',
            name:'hotel',
            multi:true,
            options: hotelData.map(hotel => ({
                value:hotel.slug,
                name:hotel.hotelName,
            }))
        }
    ],
  },
}

Result in the Suncel's builder

Once in the builder, you can create a Richtext blocks to put your unique content. You can as well add the block to display the hotel data by simply clicking on their slug in the multi select settings of the block. 

There is no need to fill in the hotel data each time in the interface, the components insert them as soon as you have selected the hotels to compare.

In this example, there is little data. But for real cases with a lot of data, the use of blocks fed by the data allows to save a considerable amount of time in the construction of the pages!

Builder view :

semi programmatic seo example