Rendering a Page
Alex Edwards
Posted on June 13, 2020
In Part II I covered the prototyping and implementation of writing components, today I'm going to cover how to create Pages from Contentful.
For this, I had to learn precisely how Contentful manages its database-style CMS, how the source plugin pulls information, and how base to utilise it.
My thoughts behind the difference between Page and Template are quite simple:
A Page is purpose-built and has no ambiguity to what is displayed. Meaning a unique content-model for each and every page. A Template however, has to be generalised by design. A Template for my posts may include many different elements, components even, but I'll get onto that later. Likewise, some fields might be optional, but the key aspects need to be assured requirements. That might include elements such as a unique slug, or a publish date modifiable by the user.
The Model
'Top Level Page?' and Index came around because of a need in the code rather than instinct. Contentful generates its Indexes with UUID on every field; however, if it's a top-level page (think like a site map), then you would want to give it an index of importance.
The other two exciting fields are Meta and Blocks. Meta is another content model that exists globally. Ensuring that all content has the right information, and unique where needed, making sure things like publishing dates are correct, and slugs don't override each other.
Page Blocks are the unique aspect for every page. A new content model needed for every page.
My homepage needs nothing more than a greeting, subtext and a little about myself, which I'll include in my Author Bio; which in turn also houses all my social links and ways to contact me.
Okay, so now the content model is established.
The Code
Removing all the trailing curly-brackets, this is what I'm querying for:
query Homepage {
page: contentfulPage(title: {regex: "/homepage/i"}) {
title
blocks {
... on ContentfulPageHome {
title
subTitle
authorBio {
blurbTitle
blurbDesc {
blurbDesc
}
blurbLead
For simplicity, I've removed some styling choices, but this is what it rendered as in my TSX file:
interface Props {
data: PageHome
}
const IndexPage = ({ data } : Props) => {
const blocks = data.page.blocks;
const subBlack = blocks.subTitle?.split(' ').slice(0, 4).join(' ')
const subColor = blocks.subTitle?.split(' ').slice(4).join(' ')
return (
<>
<div>
<h1>{blocks.title}✌️</h1>
<h5>{subBlack} <span>{subColor}</span></h5>
</div>
<div>
<div>
<img />
</div>
<div>
<h2>{blocks.authorBio.blurbTitle}</h2>
<p>{blocks.authorBio.blurbDesc?blurbDesc}</p>
</div>
</div>
</>
)
}
Components with page queries necessarily have two elements to the file, the query variable, then the functional component that Gatsby passes the query into, via the data
prop. Using Typescript, that data has an interface; otherwise, the TS Compiler will throw a hissy-fit. Typescript can get extensive, yet gives you complete confidence what elements are present, optional, and otherwise.
interface PageHome extends Page {
blocks: HomeBlocks
}
interface HomeBlocks {
title: string
subTitle?: string
authorBio: Bio
}
Some bits are missing here, such as the Page
& Bio
interfaces. However, each unique 'block' takes the original 'Page' with all the common elements and adds additional types where needed. Per the content model, subTitle hasn't deemed it as a requirement, so both interface and component need to cover that.
The next step is tricky, so I'll be covering that in its post; How I implemented rendering assistants to display typography, hyperlinks, and images into the code.
This post is an ongoing series along with my site development. All current process on my website and in the open-source repo is open for review. As I publish new features (Posts and Projects templates, changes in style), I'll continue to add to this series as a reflection of what I've learnt, and what reflections I have on the process.
Posted on June 13, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.