Content has not been updated for more than 2 years, proceed with caution.
Building a sub-menu with Gatsby
You have a great Gatsby site! You followed all the tutorials and it is blazing fast. But wait, you need a sub-menu (or dropdown menu) in your navigation?
It is not as easy as it sounds if you want to keep a modern component architecture and use things like the javascript .map method.
This is a real world coding problem I ran into when working on Catalyst themes and thought others may benefit from what I learned. Related documentation can be found in the schema customization API.
The other way
It is worth mentioning that you could hardcode a navigation menu, including sub-menus, for your site. It would work really well in fact. What I am going to review in this post is aimed at theme authors or developers working with larger sites where you manage the navigation menu from the gatsby-config.js
file or a CMS.
Example siteMetadata
Here is the gatsby-config.js
data structure we will be using in this example. This should look familiar for most readers, note the subMenu
array.
Schema Inference
The core challenge in building a sub-menu with Gatsby is rooted in schema inference.
By default Gatsby tries to infer the GraphQL schema for your project, and it does a really great job in most cases. However, once you start dealing with more complicated data structures and larger sites it is best practice to define the GraphQL schema for Gatsby. The pattern I have noticed working with Gatsby is that if a field is sometimes present this poses problems with the schema inference.
In this example the subMenu
array is sometimes present and sometimes not. Therein lies problem.
Defining the fields
Instead of allowing Gatsby to infer the fields we need to define them using the create types API in gatsby-node.js
to tell Gatsby exactly what the data structure will be. This gatsby-node.js
file needs to be in your final site (and not a theme), otherwise you will get build errors when you try to deploy. If you are defining the fields from a CMS it would be similar to this.
When using this API capitalization matters, and also note that I did not include @dontInfer
to opt out of type inference. I am letting Gatsby handle inferring all of the other fields in siteMetadata
but I am explicitly telling it to about the menuLinks
and subMenu
fields.
Handling null fields and creating default values
Did you see the exclamation points beside some of the fields above? Remember that this tells Gatsby whether the field can be null or not. An exclamation point means the field is non-nullable. It has to have a value. If you don’t want a field to be null, but instead want to have a default or placeholder value for the field you can do this with the create field extension API. Let’s add in a default value for our subMenu so instead of null it is an empty array. This will become critical later when we want to map the array.
Query Results
Now when the menuLinks
array is queried with GraphQL we get the following data structure in return, notice the empty subMenu
array on the page-1 and the page-3 link.
Build the menu component
We have created a dependable data structure that we can trust! That is a big deal!
Now you are free to use common javascript, React and CSS patterns to create the navigation menu component, including sub-menus. Don’t forget :focus-within
, aria-haspopup="true"
and aria-label="submenu"
when building your menu for accessibility. You can use conditional rendering to implement proper aria and map over the sub-menus. There is a good article on CSS tricks that covers properly styling a basic menu.
Here is a minimal implementation of a menu and sub-menu with no styling so you you can see the element structure. In this example menuLinks
is the variable which points to the queried menu data (see above).
Here is a full implementation of a menu and sub-menu with basic styling using Theme UI and the sx
prop. Again, in this example menuLinks
is the variable which points to the queried menu data (see above).
Happy coding!