-
SummaryGatsby is adding a new API that we are calling “Slices”. By using a new MotivationGatsby already supports Incremental Builds for speeding up content changes for individual (or a subset of) pages, but what if there’s a common component across all pages? Today, when a change to the content of that component happens, every page using that component needs to be rebuilt. The goal of this feature is to provide new ways to speed up builds. While there is a lot going on behind the scenes to make Slices work, we want to provide an easy-to-use API for Gatsby sites to take advantage of this. DesignWe’ll be adding a new concept to Gatsby called “Slices”. This will come with a Note: Nested Slices will not be supported. Using the FilesystemSimple
With Changing Header
Using the APIImplementing a slice will take the following flow:
Once these slices are created, any time Gatsby encounters a Prior Art
ExamplesNavigation Bar (Simple)In a very simple example, let’s take a site that’s mostly handled with filesystem routes, but with a navigation bar who’s banner content comes from our CMS. Right now, whenever that banner content changes, every page needs to rebuild to reflect the changes. Instead, we just want to create a single slice for the navigation bar. Using the Filesystem
import React from "react";
import { Slice } from "gatsby";
const DefaultPageLayout = ({ navbarClassname = '', children }) => {
return <>
{/* The old implementation looked like this: */}
{/* <Navbar className={navbarClassname} */}
{/* Now, just change it to the slice! */}
{/* The "alias" prop is the same as the id passed in gatsby-node.js */}
<Slice alias="navbar" className={navbarClassname} />
<div>
{children}
</div>
</>
}
export default DefaultPageLayout
import React from "react";
import { Link, useStaticQuery, graphql } from "gatsby";
export default function Navbar({ className = '' }) {
const { content } = useStaticQuery(graphql`
query BannerContent {
content: BannerContent(name: { eq: "Banner" }) {
body {
childMarkdownRemark {
html
}
}
}
}
`)
return (
/* We can also use the `className` passed from our DefaultPageLayout component */
<div className={className}>
<div
className="banner"
dangerouslySetInnerHTML={{
__html: content?.body?.childMarkdownRemark?.html,
}}
/>
<Link to="/">Home</Link>
<Link to="/store">Store</Link>
<Link to="/about">About</Link>
</div>
);
}
export default Navbar Using the API
exports.createPages = ({ actions }) => {
actions.createSlice({
id: `navbar`,
component: require.resolve(`./src/templates/navbar.js`),
});
}
import React from "react";
import { Slice } from "gatsby";
const DefaultPageLayout = ({ navbarClassname = '', children }) => {
return <>
{/* The old implementation looked like this: */}
{/* <Navbar className={navbarClassname} */}
{/* Now, just change it to the slice! */}
{/* The "alias" prop is the same as the id passed in gatsby-node.js */}
<Slice alias="navbar" className={navbarClassname} />
<div>
{children}
</div>
</>
}
export default DefaultPageLayout
import React from "react";
import { Link, useStaticQuery, graphql } from "gatsby";
export default function Navbar({ className = '' }) {
const { content } = useStaticQuery(graphql`
query BannerContent {
content: BannerContent(name: { eq: "Banner" }) {
body {
childMarkdownRemark {
html
}
}
}
}
`)
return (
/* We can also use the `className` passed from our DefaultPageLayout component */
<div className={className}>
<div
className="banner"
dangerouslySetInnerHTML={{
__html: content?.body?.childMarkdownRemark?.html,
}}
/>
<Link to="/">Home</Link>
<Link to="/store">Store</Link>
<Link to="/about">About</Link>
</div>
);
}
export default Navbar Now we have a site where the navbar is pulling in banner content from the CMS, but whenever the content is changed outside of Gatsby, we’ll only rebuild and deploy the navigation bar once. The pages no longer need to be rebuilt! Blog Authors (Complex)Here we have a blog site where markdown files control our blog content and another source is providing information about authors. We want to create a slice for every author.
exports.createPages = async ({ actions }) => {
// Query for all authors from some source
const authorResult = await graphql(
`
{
allAuthor {
edges {
node {
id
}
}
}
}
`
)
// Create slices for each author
authorResult.data.allAuthor.edges.forEach(({ node }) => {
actions.createSlice({
// Give each author a unique ID
id: `author-${node.id}`,
component: require.resolve(`./src/templates/author-details.js`),
context: {
id: node.id,
},
})
})
// Query for all blog posts
const blogResult = await graphql(
`
{
allMarkdownRemark(limit: 1000) {
edges {
node {
frontmatter {
path
authorId
}
}
}
}
}
`
)
// Create blog pages for each blog post
blogResult.data.allMarkdownRemark.edges.forEach(({ node }) => {
createPage({
path: node.frontmatter.path,
component: require.resolve(`./src/templates/blog-post.js`),
context: {
pagePath: node.frontmatter.path,
},
// Set 'author' slice alias for this page to use the author-${authorId} slice
slices: {
author: `author-${node.frontmatter.authorId}`
}
})
})
}
import React from "react";
import { Slice } from "gatsby";
import { Nav } from "../components/nav";
const BlogPost = ({data}) => {
return <>
<Nav />
/*
Set the 'alias' prop as author, which will be filled by the
correct slice from `createPage`. We can also set other props!
*/
<Slice alias="author" className="blog-author" />
<div>
<section
dangerouslySetInnerHTML={{ __html: data.markdownRemark.html }}
itemProp="articleBody"
/>
</div>
</>
}
export const pageQuery = graphql`
query BlogPostBySlug(
$id: String!
) {
markdownRemark(id: { eq: $id }) {
html
}
}
`
export default BlogPost
import React from "react";
import { graphql } from "gatsby";
// Here we can define a slice query using slice context (author id) to get details about author
export const sliceQuery = graphql`
query (id: String!) {
author(id: { eq: $ID }) {
name
twitter
email
}
}
`
export default function AuthorDetails({ data, className = '' }) {
const { author } = data
return (
/* We can also use the `className` passed from our BlogPost component */
<div className={className}>
<div>Written by {author.name}</div>
<div>Twitter: {author.twitter}</div>
<div>Email: {author.email}</div>
</div>
);
}
export default AuthorDetails Now, if our CMS issues a change to an author’s twitter handle, Gatsby doesn’t have to rebuild every post that author has created - just the small Alternatives
Adoption Strategy
Considerations
How can you help?A few questions we’re looking to get answered:
We’ll also be publishing a canary out before the Gatsby 5 Beta. Trying that on your sites would be a great source of feedback for us! Thank you for your thoughts, and thanks for using Gatsby! |
Beta Was this translation helpful? Give feedback.
Replies: 10 comments 15 replies
-
There are two common performance/size issues which sites that generate pages on a template via a CMS face, that I wonder if these would be able to additionally solve. Firstly, if the CMS provides a lot of building blocks that a page can conditionally add or not, these all enter the bundle. This can be resolved by lazy loading, but I wonder if these could be bundled separately as a "Slice bundle" out of the box. Secondly, any static queries that are used within any component on any page using that template (due to bundling etc) are then included on every page using that template. This increases the amount of data that is downloaded when loading the page. If the above is done where slices are separated from a bundle perspective, would it also be possible to only included static queries that slices pull in on pages that actually use the slice? It's relatively common for sites to use a "Page" template type with a bunch of pre-setup components that can be added via a CMS, many of which might make use of static queries, so this can quite quickly become a large issue. |
Beta Was this translation helpful? Give feedback.
-
This Festure looks very promising ✨ I assume that src/slices will be supported from within Gatsby Plugins – just as src/pages are. |
Beta Was this translation helpful? Give feedback.
-
This sounds like a useful feature. I don't have any comments on the implementation details, but I wanted to throw in a quick comment about the "Slices" name. Prismic CMS uses the term "Slices" to represent page template fragments that can be combined to build a page. This is essentially what @me4502 described in her comment. More information about Prismic Slices can be found here: What is a Slice? - Prismic While I understand Prismic's Slices feature and Gatsby's proposed Slices feature solve different problems (at least in their intention), overloading the term "Slices" could be confusing for existing Gatsby + Prismic users. In addition to the name confusion for Prismic developers, Prismic recommends using the If the name is truly not set in stone, would you consider a rename? Perhaps to something that more directly implies its purpose, such as "Partial."
|
Beta Was this translation helpful? Give feedback.
-
I think the name "Slice" is great. It's really effective in communicating the motivation and purpose of the API since it's literally slicing your data layer orthogonally to pages/templates. 🙂
The proposed name and convention overlapping with an existing Prismic API is unfortunate, but I think that renaming the Gatsby Slice API to "Parts" or "Partials" could accidentally cause people to confuse it as a best practice when really it's more of an escape hatch. The Prismic API is already pretty unstable so they might end up renaming their slices to something more straightforward like sections anyways. |
Beta Was this translation helpful? Give feedback.
-
I like the name 🍕 Slices 🍕 (Who doesn't want to be reminded of pizza while they code??) On large sites not having to rebuild every page just because a link in the footer changed its going to be a big win. We also do a lot with cross-linking... e.g you might have a page built up of components with one of them being a featured list of guides I can see this approach working well for that. I can see being able to build specific components out with |
Beta Was this translation helpful? Give feedback.
-
Hi! This is a neat idea and we can't wait to test it!
I find the usage of the word Slice unfortunate in this context. Slice often means cutting a list or getting a subset of a collection. See javascript slice and python slice I wonder if "fragment" would be more appropriate. See Another word that comes to mind is partials.
Just to make sure I understood correctly, the goal of this RFC is to optimize the scenario where the same content changes on many pages. If we change the code of the slice, it would still trigger a full build right? |
Beta Was this translation helpful? Give feedback.
-
Has something changed? I just tried to use slices in 4.24.3 and I can't seem to find the Slice component. Or are things being changed as V5 is getting close? |
Beta Was this translation helpful? Give feedback.
-
Hey everyone! Thank you to all who have tested the Gatsby Slice API. We're excited to announce that Gatsby 5 will be out soon and launch with the Gatsby Slice API. Here are a few docs to get you started upon launch:
Note: The initial launch of this feature will not include the Filesystem Routes path of using Slices. Feel free to hop into our Discord if you have any questions. Thank you! 💜 |
Beta Was this translation helpful? Give feedback.
-
This is released in Gatsby 5 now 🎉 https://www.gatsbyjs.com/docs/reference/release-notes/v5.0/#slice-api |
Beta Was this translation helpful? Give feedback.
-
@LekoArts how can I create the "slices map" for pages created with the filesystem routing instead of the
|
Beta Was this translation helpful? Give feedback.
This is released in Gatsby 5 now 🎉
https://www.gatsbyjs.com/docs/reference/release-notes/v5.0/#slice-api