Skip to content

Add @weaverse/hydrogen/portable-text helper (Phase 3 of weaverse-builder#2208) #449

@paul-phan

Description

@paul-phan

Background

weaverse-builder#2208 added native Portable Text output to the Content API:

  • GET /pages/:type/:handle?format=portable-text
  • GET /theme-settings?format=portable-text
  • Accept: application/portable-text+json

(Implementation: Weaverse/builder#2236)

This issue tracks the SDK-side convenience helper, originally Phase 3 of the plan. It was deferred from the Builder PR because it lives in this repo.

Scope

Add a small helper to @weaverse/hydrogen that lets developers convert an existing weaverse-format page payload to Portable Text on the client/edge — useful for codebases that haven't switched their server-side fetch to ?format=portable-text yet, or that need both formats at once.

import { toPortableText } from '@weaverse/hydrogen/portable-text'

const ptContent = toPortableText(page.items, { meta: true })

The conversion logic is identical to the server-side serializer in builder/app/backend/content-api/portable-text/ — we should port (or vendor) the pure module, not duplicate it.

Deliverables

  1. New subpath export: @weaverse/hydrogen/portable-text exposing:
    • toPortableText(items, options?)
    • htmlToPortableText(html)
    • richtextValuesToPortableText(value) (for theme settings)
    • Type exports: PTBlock, PTSpan, PTMarkDef, etc.
  2. Same options surface as Builder: { meta?: boolean; keyLength?: number }.
  3. Tree-shakeable — pure module, no React or Hydrogen imports.
  4. Tests mirroring the Builder unit tests.
  5. Docs example (in the existing Hydrogen docs) showing rendering with @portabletext/react against either:
    • server-fetched PT (?format=portable-text), or
    • client-converted PT (using this helper).

Implementation notes

  • The Builder serializer uses node-html-parser (works in both Node and edge runtimes — no DOM dependency). Should work as-is for Hydrogen / Oxygen / Cloudflare Workers.
  • Builder source to port: builder/app/backend/content-api/portable-text/ (6 files, ~670 lines incl. types).
  • Builder spec with full design + decisions: builder/.specs/2026-05-02--portable-text-content-api/.
  • Follows the fixed-version group release rhythm (core → react → hydrogen) — this would land as a hydrogen-only minor.

Non-goals

  • No HTML output format — the issue explicitly deferred that.
  • No schema-driven richtext detection — the same heuristic (isHtml) used in the Builder is fine for v1.
  • No round-trip helper (PT → weaverse) — not requested; the ?meta=true _weaverse.id is enough for consumers who need to map back.

Priority

Low — the server-side endpoint already returns PT directly via ?format=portable-text. This is a convenience for adoption, not a blocker. File when there's user demand.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions