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

@kavishkagaya/json-render-docx

v0.1.1

Published

DOCX renderer for @json-render/core. JSON specs become Word (.docx) documents.

Readme

@kavishkagaya/json-render-docx

CI

A DOCX renderer for json-render — turn a json-render spec into a Word (.docx) document, the same way @json-render/react-pdf turns one into a PDF.

json-render lets you generate UIs and documents from a single, catalog-constrained JSON spec. There are official renderers for React, React Native, PDF, HTML email, images, video, and terminals — but none for DOCX. This package fills that gap: it reuses the shared @json-render/core spec contract and engine semantics (visibility, $bindState/$item bindings, repeat) and maps the tree onto native docx primitives.

Not affiliated with Vercel. The @json-render/* scope is owned by Vercel Labs; this is a community renderer built on the public @json-render/core.

Install

npm install @kavishkagaya/json-render-docx @json-render/core docx zod

Quick start

import { renderToBuffer } from '@kavishkagaya/json-render-docx';
import type { Spec } from '@json-render/core';

const spec: Spec = {
  root: 'doc',
  elements: {
    doc: { type: 'Document', props: { title: 'Service Agreement' }, children: ['sec'] },
    sec: { type: 'Section', props: { size: 'A4' }, children: ['h', 'p', 'tbl'] },
    h: { type: 'Heading', props: { text: 'Service Agreement', level: 1 }, children: [] },
    p: { type: 'Paragraph', props: { text: 'This agreement is made between the parties.' }, children: [] },
    tbl: {
      type: 'Table',
      props: {
        columns: [{ header: 'Item', width: 60 }, { header: 'Price', width: 40, align: 'right' }],
        rows: [['Consulting', '1000'], ['Support', '500']],
      },
      children: [],
    },
  },
};

const buffer = await renderToBuffer(spec);
// write it, stream it, or hand it to a download

Render API

import { renderToBuffer, renderToBlob, renderToStream, renderToFile } from '@kavishkagaya/json-render-docx';

await renderToBuffer(spec, options);          // Node Buffer
await renderToBlob(spec, options);            // browser Blob (download path)
renderToStream(spec, options);                // pipe to an HTTP response
await renderToFile(spec, './out.docx', options); // straight to disk (Node)

options: { registry?, includeStandard?, state?, context? }.

  • state — initial state model for $bindState, visibility, and repeat.
  • registry — custom components, merged over the standard ones.
  • context — a renderer-wide value handed to every component (e.g. a theme). DOCX is walked bottom-up with no React context, so this is how you thread shared config.

Standard components

The same set @json-render/react-pdf ships (Document, Row, Column, Heading, Text, Table, List, Image, Link, Divider, Spacer, PageNumber), matched here so a document-shaped spec is portable between PDF and DOCX. Page — react-pdf's name for its page unit — is a registered alias for Section, DOCX's real page unit; use either name.

| Component | Maps to (docx) | |-----------|------------------| | Document (root) | Document — metadata + list numbering | | Section (alias: Page) | a section: page size, orientation, margins | | Heading | Paragraph + HeadingLevel | | Text | TextRun (inline; block-wrapped if standalone) | | Paragraph | Paragraph (nest Text/Link as runs) — DOCX-only, see below | | Link | ExternalHyperlink | | Table | Table / TableRow / TableCell | | Row | borderless single-row table (flexbox emulation); gap becomes cell margins on the shared inner edge | | Column | vertical flow (the default); also the element to attach repeat/visible to when they apply to several siblings at once | | List | bulleted or numbered paragraphs | | Image | ImageRun (data URI/base64) | | Divider | bottom-bordered paragraph | | Spacer | spacing-only paragraph | | PageBreak | PageBreak | | PageNumber | PAGE / NUMPAGES fields |

Styling is configurable the same way react-pdf does it: per-instance props on each component (color, thickness, spacingBefore/spacingAfter, headerBackground, striped, ...) — not a shared theme/context mechanism. No renderer in the json-render ecosystem has one; matching that keeps specs portable across renderers rather than inventing a docx-only convention.

Every component is the same kind of function — standardComponents.Paragraph(props, children) works whether you're the engine or a custom component composing it.

Prefer Text over Paragraph for body copy if the spec also needs to render as PDF. Verified directly: rendered the same spec through both this package and @json-render/react-pdf — react-pdf has no Paragraph component at all, so any element using it is silently dropped there (confirmed missing from both the PDF's text and a rendered screenshot), while Text used at block level renders correctly on both (it's automatically wrapped in its own paragraph here, and Text already covers bold/italic/underline/strike/color/size/font). Reach for Paragraph only for genuinely DOCX-specific needs — indentLeft, or nesting several Text/Link runs into one paragraph.

Custom catalog

A component is one typed function — (props, children, context) => DocxRenderResult — nothing more. The engine calls it that way when a spec references it, and it's the exact same call a component makes to compose a sibling component. There's no separate "builder" API to learn: dropping a raw docx primitive in and calling a base component you already have work the same way.

import { defineCatalog } from '@json-render/core';
import { schema, standardComponentDefinitions } from '@kavishkagaya/json-render-docx/server';
import { defineRegistry, renderToBuffer, standardComponents } from '@kavishkagaya/json-render-docx';
import { z } from 'zod';

const catalog = defineCatalog(schema, {
  components: {
    ...standardComponentDefinitions,
    SignatureBlock: {
      props: z.object({ name: z.string(), title: z.string().nullable() }),
      slots: [],
      description: 'A signature line: a divider, a bold name, a role underneath.',
    },
  },
});

const { registry } = defineRegistry(catalog, {
  components: {
    ...standardComponents,
    // Composed from the base components you already have — call them
    // directly, same as the engine does.
    SignatureBlock: (props) => ({
      kind: 'group',
      nodes: [
        standardComponents.Divider?.({ thickness: 1 }, []),
        standardComponents.Paragraph?.({ text: props.name, bold: true }, []),
        props.title ? standardComponents.Text?.({ text: props.title, color: '#6b7280' }, []) : null,
      ].filter((n) => n != null),
    }),
  },
});

const buffer = await renderToBuffer(spec, { registry });

If a base component's props don't expose what you need (e.g. arbitrary block content per table cell), drop to raw docx directly instead — a component returns a DocxNode (or an array / null):

  • { kind: 'run', run } — inline TextRun / ExternalHyperlink
  • { kind: 'block', block } — a Paragraph or Table
  • { kind: 'section', section } — an ISectionOptions (a page)
  • { kind: 'document', document } — a fully-built docx.Document (root only)
  • { kind: 'group', nodes } — a flat list of the above

The assembler coalesces consecutive runs into paragraphs and bare blocks into a default section, so you rarely have to think about it.

Server-safe import

Import the schema and catalog for AI prompt generation / spec validation without pulling in the docx runtime:

import { schema, standardComponentDefinitions } from '@kavishkagaya/json-render-docx/server';

Limitations

Word is not CSS. Two deliberate, documented gaps:

  • No flexbox. Row is emulated with a borderless table; Column is plain vertical flow. There is no flex/align/justify layout.
  • No remote image fetch. Images must be data URIs or base64. An http(s) src degrades to a labelled hyperlink rather than failing.

License

MIT © Kavishka Rambukwella