Template Parts
Reusable site sections powered by blocks
Template parts are reusable block-backed sections of a theme, such as a site footer, site header, menu, or global callout. They are edited in WordPress, queried as app data, and rendered anywhere in React with ContentBlocks.
Use template parts for content that belongs to the site shell rather than one page.
Define A Template Part Block
Set templatePart on the block metadata.
import { defineBlock, EditableText } from "eddev/blocks"
import { Link } from "eddev/routing"
export const meta: BlockMeta = {
title: "Site Footer",
inserter: false,
templatePart: {
area: "footer",
slug: "siteFooter",
},
}
export default defineBlock("parts/footer", (props) => {
return (
<footer>
<Link href={props.contactLink ?? "/contact"}>
<EditableText store="contactLink" defaultValue="Let's have a chat" />
</Link>
</footer>
)
})slug becomes the field name under templateParts in GraphQL. area tells WordPress what kind of template part this is. inserter: false is usually right, because authors should edit the template part itself rather than adding this block manually to pages.
Add A Block Query
Template part blocks can have their own paired GraphQL files, just like normal blocks.
query {
block {
parts_footer {
contactLink
}
}
}Query The Template Part
Template parts can be queried anywhere a normal GraphQL file can select templateParts.
For site shell content, select the template part from app data so it is available around every route:
query CommonData {
templateParts {
siteFooter {
contentBlocks
}
}
}Then render it with ContentBlocks.
import { ContentBlocks } from "eddev/blocks"
import { useAppData } from "eddev/hooks"
export function Footer() {
const footer = useAppData((data) => data.templateParts?.siteFooter)
return <ContentBlocks blocks={footer?.contentBlocks} />
}Finally, include the component in _app.tsx so it appears around every route.
import { Footer } from "@features/site/Footer"
import { RouteDisplay, useRoute } from "eddev/routing"
import { defineView } from "eddev/views"
export default defineView("_app", () => {
const route = useRoute()
return (
<>
<RouteDisplay route={route} />
<Footer />
</>
)
})You do not have to use _app.graphql or useAppData. Blocks and views can query template parts directly when they need globally-defined content in a specific place.
query CalloutBlock {
block {
content_callout {
heading
}
}
templateParts {
siteFooter {
contentBlocks
}
}
}That pattern is useful for callouts, reusable panels, or other blocks that need shared content without making it global app data.
eddev automatically constrains the matching template-part editor to the block declared in templatePart, so the editing surface stays focused.
When To Use This
Template parts are a good fit for:
- site headers and footers
- global navigation or mega menus
- reusable site-wide panels
- shared error page content
- content that should be edited once and rendered on many routes
Do not use template parts for ordinary per-page blocks. If content belongs to a page or post, keep it in that page or post's contentBlocks.