--- myst: html_meta: "description": "" "property=og:description": "" "property=og:title": "" "keywords": "" --- # Dynamic pages In previous chapters, we learned how: - Create static pages - Fetch data from external sources - Query data and show it in pages We could potentially build our static websites just with that information. The only problem is that we need to manually create every single page that we need. The last missing part in building a static website with GatsbyJS is to programmatically generate these pages from data. As we have seen in the {doc}`pages` chapter, at build time GatsbyJS maps every page with an URL that is its filename. If we want to programmatically generate static pages, we are not going to have different page components (one for each node), but only GraphQL nodes. We need to provide some additional information in nodes to allow GatsbyJS to generate the correct pages and URLs. In particular we need to add a "slug" or "path" attribute to the node. ```{note} A lot of source plugins automatically add this information in nodes, like CMS plugins. ``` The GatsbyJS building stack has a sequence of steps that perform different actions: - Read the configuration to load the list of plugins - Initialize the cache (to avoid re-fetch untouched data) - Pull the data and preprocess it in a GraphQL schema - Create pages (from `/pages` folder or from plugins) - Extract and run GraphQL queries and replace their values in pages - Write out the pages as static HTML pages GatsbyJS provides a rich set of "lifecycle APIs" to hook into every step and perform some customizations. In this chapter we are going to use two of these APIs, which are the most used in plugins: - `onCreateNode`: called by GatsbyJS whenever a node is created or updated, so we can edit the current node before storing it into GraphQL - `createPages`: step that creates a page. To implement an API, we need to export a function with the same name of the API in a file called `gatsby-node.js`. Let us start with the first one, and export a function called `onCreateNode`: ```{literalinclude} _snippets/gatsby-node.js :language: jsx :lines: 1-3 ``` This function is called whenever a node is created. If we restart the server now, we will see that in the console we will have the types of every node. We want to add a slug only for nodes generated by the MarkdownRemark plugin. We need to filter them by type. To generate the slug for this node, we could use a helper funcion from `gatsby-source-filesystem` that is made for this purpose: `createFilePath`. ```{note} If you remember, Remark nodes are built on top of filesystem nodes. ``` Finally we need to add the slug attribute to the node. Nodes can be directly modified only by the plugins that created them. Other plugins can add new fields to the nodes only with a specific function called `createNodeField` (for security reasons). And finally, our `onCreateNode` function will be similar to this: ```{literalinclude} _snippets/gatsby-node.js :language: jsx :lineno-match: true :lines: 5-19 ``` If we restart the server and try to query the data, we will see the slug under the `fields` attribute. Now that we have all the information, we need to create pages. As mentioned in the introduction, to create a page we need to query data with GraphQL and then map the results into a page. To do this, we need to export the other mentioned function (`createPages`) in `gatsby-node.js` file: ```{literalinclude} _snippets/gatsby-node.js :language: jsx :lineno-match: true :lines: 21-49 ``` What can we see here? First of all we perform a GraphQL query, and we iterate through the results to create a new page. The method `createPage` is a helper method that GatsbyJS uses to generate dynamic pages. It takes 3 parameters: `path`: the slug value. This is used to generate the URL where we can access the current page. `component`: the template used to populate a blog post page. It is similar to a page component (we will see it shortly). `context`: we can pass a list of variables that can be used by the queries into page components (not `StaticQuery`) to fetch information about the current node. A this point we create the `blog-post.js` template file to end our setup: ```{literalinclude} _snippets/blog-post.js :language: jsx ``` This is similar to a simple page component, except for GraphQL query. We need to fetch data for a specific node. To do this, we can use the `slug` value to filter only desired node. ```{note} We can filter with almost every node attribute, but it is always better use uniques values like `id` or `slug`. ``` ```{note} `dangerouslySetInnerHTML` is a helper function of ReactJS that allows to insert some not-reactish HTML into a component. ``` If we restart the server, we can now directly access the pages that were automatically created. ```{note} To easily get a list of generated URLs, try to access a random page like [http://localhost:8000/asdf](http://localhost:8000/asdf). The default `NotFound` page will offer alternative URLs. ``` Last thing that we could do is to link them in our `index.js` page: ```{literalinclude} _snippets/index_posts.js :emphasize-lines: 13,20 :language: jsx ```