Skip to content

A richtext value renders as a BLANK grid cell — the type is routed to MarkdownCellRenderer, whose pipeline drops the stored HTML #5452

Description

@os-support-ai

Found while measuring objectui#5415 (record-grid row heights); a different defect class, filed separately and not fixed there.

Measured

Real browser (Chromium, apps/console vite dev, ObjectGrid with inline data and a mock getObjectSchema). One row, one column, the stored value is the showcase seed's own richtext specimen (examples/app-showcase/src/data/seed/index.ts, f_richtext):

value stored: p-wrapped Rich + strong-wrapped text

column field type cell textContent cell inner HTML
richtext "" (empty) div.prose … empty
html Rich text div.prose containing the real p / strong markup
markdown (control, markdown-shaped input **bold** and _em_) bold and em rendered markup

So a populated richtext field shows the user nothing at all in a grid cell. Neighbouring html — same stored bytes, different declared type — renders it correctly, which is what rules out "the value never arrived".

Mechanism

packages/fields/src/index.tsx, getCellRenderer's standard map:

markdown: MarkdownCellRenderer,
html:     HtmlCellRenderer,
richtext: MarkdownCellRenderer,   <-- here

richtext stores HTML (that is what RichTextField writes, and what the showcase seed carries), but it is dispatched to the markdown renderer, which hands the string to the sanitizing GFM pipeline (MarkdownContent). Raw HTML is dropped there, and since the value is entirely HTML, everything is dropped and the cell renders empty. The title attribute on the shared cell wrapper still carries the raw string, so the value is reachable on hover — which is why this reads as a styling nit rather than data loss until you look at the cell body.

HtmlCellRenderer already exists two functions below and handles exactly this shape (it sanitizes with sanitizeHtml and keeps the markup).

Why it matters

Failure direction is the bad one: no error, no fallback, no console warning — a populated field reads as an empty field. Anyone auditing data through a grid concludes the records are blank.

Note

Do not "fix" this by making the markdown pipeline pass raw HTML through — that would change every markdown cell's trust boundary. The question is which renderer richtext should map to, and that is a one-line decision in the map above (plus whatever the read side of the detail page does with the same type — not checked here).

Repro harness was untracked and removed; it is ~40 lines of ObjectGrid with data: { provider: 'value', items } plus a getObjectSchema stub, following the existing apps/console/src/dev/DevRowActions.tsx pattern.

Activity

  1. added theissue type on Aug 21, 2026
  2. os-zhuang commented on Aug 21, 2026

    @os-zhuang
    Contributor

    Triage: queued, type Bug — a populated richtext field renders as an empty grid cell (silent data-invisibility, measured with an html-typed control on the same bytes). Fix locus is the getCellRenderer map in packages/fields/src/index.tsx; the body's ⛔ (do not loosen the markdown pipeline's trust boundary) is the load-bearing constraint, and the detail-page read side of the same type should be checked in the same diff. Size/model suggestion: S, sonnet.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions