npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bnomei/emdash-bento

v0.2.1

Published

Bento grid field widget for EmDash JSON fields, using Block Builder for nested blocks.

Readme

@bnomei/emdash-bento

npm version npm downloads license types source

Bento grid field widget for EmDash JSON fields.

@bnomei/emdash-bento is a native EmDash plugin for row and column layout data stored as plain JSON. It registers the bento:layouts admin widget, reuses @bnomei/emdash-blocks for nested column content, and exports helpers for Astro renderers to prepare visible rows and translate fractional spans into a 12-column grid. Use it when editors need controlled layout composition while templates still own the frontend markup.

What It Provides

  • Native EmDash plugin factory: bentoPlugin().
  • JSON field widget: bento:layouts.
  • One editable layout pattern string per row.
  • Nested block controls through options.blockDefinitions.
  • Admin UI built with Kumo UI with full light and dark mode support.
  • Frontend helpers: visibleLayoutRows() and spanToGridColumns().

Install

npm install @bnomei/emdash-bento @bnomei/emdash-blocks

Register both plugins in astro.config.mjs:

import emdash from "emdash/astro";
import { bentoPlugin } from "@bnomei/emdash-bento";
import { blockBuilderPlugin } from "@bnomei/emdash-blocks";

export default {
  integrations: [
    emdash({
      plugins: [blockBuilderPlugin(), bentoPlugin()],
    }),
  ],
};

Field Widget

Use the widget on an EmDash json field:

{
  "slug": "layouts",
  "label": "Grid",
  "type": "json",
  "widget": "bento:layouts",
  "options": {
    "blockDefinitions": [
      {
        "type": "heading",
        "label": "Heading",
        "props": [{ "key": "text", "label": "Text", "type": "text" }]
      },
      {
        "type": "text",
        "label": "Text",
        "props": [{ "key": "text", "label": "Text", "type": "markdown" }]
      },
      {
        "type": "image",
        "label": "Image",
        "props": [{ "key": "image", "label": "Image", "type": "media" }]
      }
    ]
  }
}

Stored Value

type LayoutBuilderValue = Array<{
  id: string;
  layout: string;
  settings?: Record<string, unknown>;
  columns: Array<{
    id: string;
    span: string;
    blocks: BlockBuilderValue;
  }>;
}>;

Empty layout fields stay empty when the admin widget mounts, so opening an untouched entry does not write a default row or mark the field dirty. The widget shows an empty state with the Add Layout button, and clicking that button creates the first editable row.

Each row stores exactly one layout pattern. The pattern is a comma-separated list of column spans, for example 1/1, 1/2, 1/2, or 1/1, 1/3, 2/3. Editing the pattern updates the row's columns by position, preserving existing column blocks where a matching column index still exists. If the edited pattern has fewer spans than the row already has columns, columns beyond that pattern are removed. The per-column span values mirror the row pattern and are stored so frontend renderers can read the same data directly.

Editors can also append a column with the row-level Add Column button. New columns start as 1/1, and the row pattern updates to match the visible columns.

The Add Layout button creates a new row with 1/1, 1/2, 1/3; editors can change that row's layout pattern after it has been added.

Each column header also exposes a sorted width select and a remove button. The select choices are the unique spans in that row's current layout pattern. Layouts can be reordered with up/down controls. Columns can be reordered with left/right controls.

The admin widget renders columns on a 12-column CSS grid where 1/2 spans 6 grid columns and 1/3 spans 4 columns. If a row's widths add up to more than 1/1, the extra columns wrap to the next line. The widget accepts comma-separated fractions with denominators up to twelfths, so editors can type one row pattern such as 1/1, 1/2, 1/2, or 1/1, 2/3, 1/3 and override it whenever the layout needs to change.

Nested blocks use @bnomei/emdash-blocks directly. Pass options.blockDefinitions to control the available nested block types and their typed prop controls. Standalone block fields and layout column blocks share the same definition shape.

Frontend Rendering

Render layout rows as a 12-column CSS grid. The package exports helpers for the common data preparation and span calculation:

import { spanToGridColumns, visibleLayoutRows } from "@bnomei/emdash-bento";
import { visibleBlocks } from "@bnomei/emdash-blocks";

const rows = visibleLayoutRows(entry.layouts);
{
  rows.map((row) => (
    <section class="layout" data-layout-id={row.id}>
      {row.columns.map((column) => (
        <div
          class="column"
          style={`--span: ${spanToGridColumns(column.span)}`}
          data-column-id={column.id}
        >
          {visibleBlocks(column.blocks).map((block) => renderBlock(block))}
        </div>
      ))}
    </section>
  ))
}
.layout {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
}

.column {
  grid-column: span var(--span) / span var(--span);
}

The helpers accept native Bento values. Migration from other systems should happen before values are saved into EmDash.

Package Surface

  • ESM entry: @bnomei/emdash-bento.
  • Admin entry: @bnomei/emdash-bento/admin.
  • Type declarations are included from dist/.
  • Peer dependencies: @bnomei/emdash-blocks ^0.2.0, emdash >=0.17.0, react ^18.0.0 || ^19.0.0, react-dom ^18.0.0 || ^19.0.0, @cloudflare/kumo ^2.5.0, and @phosphor-icons/react ^2.1.10.

Status

This package ships as a native EmDash plugin because the editor is a trusted React admin field widget. Package exports point at Viteplus vp pack-built dist/ JavaScript and declarations; use npm run build for the production pack and npm run pack:check for the publint-verified pack check.

Related Packages

  • @bnomei/emdash-blocks provides the nested block editor used by Bento columns.
  • @bnomei/emdash-fields provides smaller JSON-backed field widgets.

License

MIT.

Screenshot

Bento screenshot