eddev
Snippets

Automated Block Layouts

Use block flags and wrapBlock to keep layout rules out of every block

Use this when the page layout should automatically wrap blocks based on block metadata, rather than making every block render its own page grid.

For the underlying APIs, see Block Flags, Querying Blocks From Views, and Nested Blocks.

Put Layout Hints In Block Metadata

Keep flags small. They are copied into the block payload and are available to ContentBlocks.

import { defineBlock, EditableText, InnerBlocks } from "eddev/blocks"

export const meta: BlockMeta = {
  title: "Accordion",
  icon: "info",
  tags: ["#subpage"],
  flags: {
    contentWidth: true,
    marginRule: "long-card",
  },
}

export default defineBlock("content/accordion", () => {
  return (
    <section>
      <EditableText as="h3" store="title" defaultValue="Enter a title" plainText />
      <InnerBlocks allowedBlocks={["core/paragraph", "core/list", "content/button-row"]} />
    </section>
  )
})
import { defineBlock, InnerBlocks } from "eddev/blocks"

export const meta: BlockMeta = {
  title: "Card Row",
  category: "layouts",
  icon: "slides",
  tags: ["#subpage"],
  flags: {
    fullWidth: true,
    marginRule: "featured-card",
  },
}

export default defineBlock("content/card-row", () => {
  return <InnerBlocks allowedBlocks={["content/card-row-item"]} />
})

Wrap Blocks In One Place

Use wrapBlock on a shared ContentBlocks wrapper so page grid rules live in one component.

import { BlocksContext, ContentBlocks } from "eddev/blocks"
import { ComponentProps, ReactNode } from "react"

function wrapBlock(children: ReactNode, ctx: BlocksContext) {
  const marginClass = ctx.current.flags?.marginRule
    ? `block-margins-${ctx.current.flags.marginRule}`
    : ""

  if (ctx.current.blockName === "core/rich-text" || ctx.current.flags?.contentWidth) {
    return <div className={`col-subpage-content ${marginClass}`}>{children}</div>
  }

  if (ctx.current.flags?.fullWidth) {
    return <div className={`full-width-block ${marginClass}`}>{children}</div>
  }

  return <div className={marginClass}>{children}</div>
}

type Props = Omit<ComponentProps<typeof ContentBlocks>, "wrapBlock">

export function SubpageContentBlocks(props: Props) {
  return <ContentBlocks {...props} wrapBlock={wrapBlock} />
}

Use The Wrapper In Views

import { SubpageContentBlocks } from "@features/layout/SubpageContentBlocks"
import { defineView } from "eddev/views"

export default defineView("page", (props) => {
  return <SubpageContentBlocks blocks={props.page?.contentBlocks} />
})

This works well when blocks need to stay reusable across different views, but each view has its own layout rules.

On this page