In this tutorial we will learn how to utilize the Crystallize graphql API as a headless CMS for our pretend tech conference website, The Conf Vault.
All the source code for this article can be found here: github.com/molebox/gatsby-crystallize-conf-example. Feel free to fork and play around with it, it can often times help to have the source code open when following a tutorial.
I've been really impressed with what Crystallize has to offer, at first it was quite the mind shift thinking about modelling my data but I really like the process of using Figma to brainstorm the models then being able to directly translate them into actual models in the Crystallize UI.
Crystallize provide the tools with which to visually present content and I found the whole process much more aligned with how I tend to think about projects before starting them. Due to the nature of the composable shapes, we as creators can put together feature rich stories with the aim of driving home our brands story, be that our personal brand or business.
Although mainly marketed as an ecommerce PIM, Crystallize is certainly capable of much more, let's take a look...
We will learn:
This article assumes prior knowledge of React and the Jamstack ecosystem.
As a Jamstack developer you are most likely familier with the concept of the headless Content Management System (CMS), a place for you to enter and store data from which a frontend will request and use it. Differentiating between them mostly comes down to how you want to interact with your stored data, via a GUI or CLI, and how to access that data, via REST or Graphql (gql) endpoints.
Marketing itself as a super fast headless CMS for Product Information Management (PIM, we're racking up those abbreviations!), it aims to enable the user to combine rich story telling, structured content and ecommerce as a single solution. But it doesn't only have to used for ecommerce solutions. Crystallize is flexible enough so that we can utilize its structured content models and create anything we like, then using it's graphql API we can access our stored data from any device, be that computer or mobile.
The UI is also super easy to hand off to a client so that they can enter in data themselves, which is a massive plus when considering which CMS to go with while working with clients.
When we whiteboard or brainstorm ideas they are very rarely linear, they don't tend to fit in square boxes, at least that is, until we manipulate those ideas to fit a given structure, one provided to us by our choice of CMS for example. Of course, a totally generic solution to modelling our content would also be very time consuming for a user to put together. Give them a set of premade tools with just the right amount of generics however and they can create what they want, in whatever shapes they please.
The content model serves as documentation and overview and connects information architects, designers, developers and business stakeholders.
The fine folks at Crystallize have created a design system using Figma and given everyone access to it via a Figma file you can download. I put together a model for our tech conf site which you can download here.
Looking at the content model, we have 3 shapes,
Speaker. These are in the format of Documents. Each one is comprised of components which make up the structure of that model. The Event shape has a relationship with both the schedule and speaker shapes. This is because an event has both a schedule and speakers. The schedule shape also has a relationship with the speakers shape. These relationships will allow us to query on a single node but access it's corrasponding relationship nodes. For example, if we query for an event, we will in turn be able to access the speakers at that event.
Note that the modelling you do in Figma can't be exported and used in the Crystallize UI, you will have to manually re-create the models.
Head over to crystallize.com and create a new account, once in create a new tenent and then you will be presented with a page similar to the following:
On the left hand side you can open the menu to reveal your options. With your Figma file open too, start creating the shapes and their components. Begin with the folders. 3 folders should do the trick,
Schedules. Now create the 3 document shapes, Event, Schedule and Speaker. Each of our document shapes will be made up of components, following our content model in Figma, add the components to the newly created shapes.
Once done open the catalogue tab (the one at the top) and inside the
Conference folder create a new document of type
Don't worry about adding anything to the schedule relationship just yet, we'll need to create a schedule first for that to make any sense! The same applies to the speakers relationships.
Once you have created all your events do the same for the speakers and schedules. Now the schedules are done you can add the speaker relations to those, then coming back to the events, you can add both the schedule and speaker relations, and the circle of life is complete!
Being a Jamstack dev there are quite a few solutions to the age old question of "Which frontend should I use for my headless CMS...?" We'll be going with Gatsby today. I prefer to spin up Gatsby sites from an empty folder, if you are well versed then feel free to use a starter or template. We'll be needing some additional packages to those that form a basic Gatsby site, from the command line (I will be using yarn but npm is fine too) add the following packages:
There are a couple of ways we could connect our Cystallize API with our Gatsby site. Crystallize has a gatsby boilerplate which uses the
gatsby-source-graphql plugin, I would have expected there to be a source plugin for sourcing data from Crystallize which would have meant abstracting away from the
gatsby-source-graphql and transforming the source nodes. Instead, we'll be super on trend and use Apollo to interact with and fetch our data.
In Gatsby there are two files that can be created and used in order to access certain points of the build process. We'll be creating a third file which will be imported into both. This is purely a personal choice that reduces code duplication, though it has become somewhat of a standard in the Gatsby community.
We create a http link to our gql endpoint and pass it to the Apollo client, before passing the client to the provider and wrapping our app.
This file will be imported into and exported from both the
gatsby-browser.js files like so:
Let's now add some scripts to our
package.json so that we can run our site.
Often times when developing Gatsby sites you will need to remove the cache, setting up a simple script to both clear the cache and run our site in gatsby develop mode will save time and headaches later. hence
yarn z, the name is arbitrary.
Now that we have Apollo setup we can head back over to the Crystallize UI and navigate to the
Catalogue Explorer tab which can be found in the left tab menu. Click
Fetch tree at root and run the query. You should see your 3 folders returned. If we inspect the query on the left of the explorer we can see that it's in fact 1 query with many fragments. These fragments split up the requests into bite size chunks that can then be spread into other fragments or the query.
A neat feature which I really like with Crystallize is the ability to test out queries directly from the shape, with provided base query and fragments to get you going. If you head to your catalogue and open up an event, then click the gql symbol which sits along the top bar an explorer will open up, it should look something like this:
This is nice and allows you to play about with different fragments and see what you would get back from your query should you use it in production. Not content with offering 2 different ways to test our queries, Crystallize provides a 3rd. A url with your tenent id which looks like the following:
This is a clean slate with tabs to save each query. From whatever gql explorer you choose, open the
Docs tab located on the right. From here you can see what you can query and how each interface is nested or relates to another. Click
catalogue and you can see that it returns a
Item, when we click the
Item we can see all of the properties we can query for.
The interesting part of this is the
children property, which itself returns an
Item. This nesting goes as far as your data is nested but is powerful and enables us to query nested children without having to specify specific properties.
For our index/home page we will be querying for the root paths to our 3 folders, these will be passed on to components which will use that path to themselves query for specific data.
We set the path param to that of the root directory, that is, the tenent. From here we ask for the first child and that's first child. So that is 2 levels deep. We request the path and the name of the shape. We know that our 3 shapes are called Conferences, Speakers and Schedules. Those should be our top level data types. Then we would expect to see the paths and shapes of the documents within the 3 folders. What is returned is the following:
Sure enough we see the expected data. Let's move back to the frontend and add this query to our code.
index.js file located in the pages folder of your Gatsby project.
Apollo provides us with a lovely way to query and handle our data. We pass our query into the
useQuery hook, in return we get 2 states (loading, error) and our data. We do a simple check to makes sure our data isn't loading or has an error then we filter out the conference paths and simply display them on the screen. We'll be coming back to this page soon, but let's first use a query that accepts some parameters.
We'll pass each conference path down to an event component which in turn will use that path as a query param to request data about that event. Let's see how that looks in practice. In your
components folder, inside the
src folder (assuming you set your project up this way) create a new file and name it
The query was put together in the gql explorer, the order of the fragments is important as some of them rely on one another and they can't be defined before they are used. The basic logic behind the query is that we pass in a path to a conference from which we want to recieve back the components that make up the data for that shape. The components are split into fragments so that our query doesn't become bloated. Notice the
relations fragment. It returns the same data as our query plus it's own path and name. Nearly recursive, of course, to understand recursion one must first understand recursion....
Schedule components follow much the same way of thinking. The
CoD and indeed some other components, uses a complimentory library supplied by Crystallize to help with displaying it's rich text data, which is returned as either
json or plain text. Let's install it and learn how to use it.
Now in our components folder create a new file named
This package basically allows us to pass in overrides for how it displays certain elements. In the above example, taken from our app, the paragraph tag is overriden with the font size prop passed in. In practice this is used like so:
And thats it. If we were to pass in the font size prop we could do so like this:
It's an elegant way to help display rich text data.
As mentioned, our
Schedule components are much the same. Let's take them both at the same time.
Our schedule component makes use of the properties table in the Crystallize backend. This is translated to key value pairs which work perfectly when used in an actual
Our site isn't much to look at, in fact it's downright ugly! But we'll worry about that later, let's first get this baby deployed and setup a web hook so that our static site rebuilds every time we publish changes from our Crystallize backend.
This section assumes you have a Netlify account setup, if not create an account if you wish to follow along with this section.
netlify.toml file at the projects root.
Next, create a new site from the repository you created earlier, I hope you have been commiting your code! Netlify will use the settings from the .toml file we just created. In the netlify dashboard head to the
Deploys tab and then the
Deploy Settings, scroll down until you find the build hooks section. Add a new build hook, naming it whatever you like, perhaps
NETLIFY_BUILD_ON_PUBLISH makes the most sense as that's what it's going to do. You will be presented with a url, copy it to the clipboard and head on over to the Crystallize UI. From the tabs on the left click the little Captain Hook icon then add a new web hook
Here we have selected publish as the event we want to trigger our build hook. Paste the url you copied from the netlify dahsboard into the URL section and change it from GET to POST, then hit save. Now make a small change to your data, add a shape, remove a full stop, whatever. Then open the netlify dahsboard, go to the deploy section and watch as your site rebuilds!
Our site quite frankly, looks terrible. Let's straighten that out. I'm going to show the code for each component plus a few extras, they each use Chakra-UI which allows inline styling via props.
Let's install some additional packages
Unfortunately Chakra requires us to install framer motion (as of v1) even though we will be adding some animations using gsap. I can forgive this as working with Chakra will enable us to utilize performant and accessibility first components and speed up our development time when creating our UI.
src folder create a new file called
theme.js this is where we will define our apps colors, fonts and font sizes.
Notice we have set the bodies visibility to hidden? We will be using some gsap animations soon and this will stop our animations from flashing on page mount.
Now we will need to add the
ChakraProvider to the
wrap-root.js file, import the theme and pass it into the
ChakraProvider like so:
Next we want to add a way to access our fonts from google. We have already installed the package so create a
gatsby-config.js file and add the following:
It's important to add the
display: 'swap' as this will swap out our font for the system font while the page loads, helping with performance.
In the components folder create two new files,
section.js . Then create a new folder called
state and add
error.js files to it.
At the moment we have a bunch of files just hanging loose in the components folder, let's organize them into something more manageble. Create a
event folder and a
hero folder. Move the
speaker.js files to the event folder. Still inside the event folder create
Cool. Now let's update our previously created components.
Most Chakra component are based on the
Box component, which itself is polymorphic and can be changed to represent any semantic html element. So in this case we have used it to re-create the html table. The upside of this is that we are able to use the Chakra props while keeping our code semantically correct.
If you now run
yarn z your website will look a damn site nicer, but it's lacking some movement. Let's spice things up with some snazzy animations. In the hero folder create 2 new files
That's quite alot of information to take in, let's step through it.
squarecomponent. It's named
topSquaresLeft, we then do the same for each corner or the page.
Finally update the
index.js file, adding the layout, hero and state components.
Thanks for taking the time to read along, if you have any questions feel free to shoot me a message on Twitter @studio_hungry