This matrix reports compile-time compatibility: for every component under ui/, whether compileJSX plus each adapter's generate() step produces diagnostics-free output. Compile-time compatibility (compileJSX + adapter generate() diagnostics). NOT render identity — rendered-output parity is owned by the adapter conformance suite and the eval vector corpus (spec/testing.md Layer 3).
62 components × 10 adapters — 620 / 620 cells compile clean
#Matrix
Each component name links to its component page (or its source, for components without one); the Description column says what it is.
| Component | Description | hono | blade | erb | go-template | jinja | minijinja | mojolicious | pebble | twig | xslate |
|---|---|---|---|---|---|---|---|---|---|---|---|
accordion |
A vertically stacked set of interactive headings that each reveal an associated section of content. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
alert |
Displays a callout for important content. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
alert-dialog |
A modal dialog that interrupts the user with important content and expects a response. Unlike… | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
aspect-ratio |
Displays content within a desired ratio. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
avatar |
An image element with a fallback for representing the user. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
badge |
Displays a badge or a component that looks like a badge. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
breadcrumb |
Displays the path to the current resource using a hierarchy of links. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
button |
Displays a button or a component that looks like a button. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
button-group |
Container for grouping related buttons with merged borders and corners. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
calendar |
A date picker component with month navigation and single or range selection. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
card |
Displays a card with header, content, and footer. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
carousel |
A carousel with motion and swipe built on top of Embla Carousel. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
chart |
Every chart component (containers, primitives, and tooltips) is JSX-native: | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
checkbox |
A control that allows the user to toggle between checked and not checked. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
collapsible |
An interactive component which expands/collapses a panel. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
combobox |
Autocomplete input with searchable dropdown. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
command |
A command menu with search, keyboard navigation, and filtering. Use it as an inline menu or inside… | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
context-menu |
A menu triggered by right-click, displayed at the cursor position. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
data-table |
Sortable column headers and pagination controls for data tables. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
date-picker |
A date picker component with calendar popup. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
dialog |
A modal dialog that displays content in a layer above the page. Supports ESC key, overlay click,… | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
direction |
A provider for setting text direction (LTR/RTL) on child content. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
drawer |
A panel that slides in from the edge of the screen, typically from the bottom. Ideal for… | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
dropdown-menu |
A menu of actions triggered by a button. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
empty |
Empty state placeholder with icon, title, and action. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
field |
Form field wrapper with label, description, and error message. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
hover-card |
A floating card that appears on hover to display rich content. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
icon |
SVG icons based on Lucide icon definitions. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
input |
Displays an input field for user text entry. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
input-group |
Input with addons, prefixes, and suffixes. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
input-otp |
An accessible one-time password input component with copy-paste support. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
item |
A generic list/menu item component with composable sub-components for building notification feeds,… | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
kbd |
Displays a keyboard key or keyboard shortcut. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
label |
Renders an accessible label associated with controls. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
menubar |
A visually persistent menu common in desktop applications. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
native-select |
A styled native HTML select element. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
navigation-menu |
A collection of links for navigating websites, with hover-activated content panels. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
pagination |
Pagination with page navigation, next and previous links. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
popover |
A floating panel that appears relative to a trigger element. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
portal |
Renders children into a different part of the DOM tree. Useful for modals, tooltips, and dropdowns. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
progress |
Displays an indicator showing the completion progress of a task. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
radio-group |
A set of checkable buttons where only one can be checked at a time. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
resizable |
Accessible resizable panel groups and layouts with drag and keyboard support. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
scroll-area |
Augments native scroll functionality for custom, cross-browser styling. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
select |
Displays a list of options for the user to pick from, triggered by a button. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
separator |
Visually or semantically separates content. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
sheet |
A panel that slides in from the edge of the screen. Extends the Dialog pattern with side-based… | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
sidebar |
A composable, collapsible sidebar component with responsive mobile support | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
skeleton |
Use to show a placeholder while content is loading. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
slider |
An input where the user selects a value from within a given range. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
slot |
A polymorphic component that merges props with child element | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
spinner |
An animated loading indicator for async operations. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
switch |
A control that allows the user to toggle between checked and not checked. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
table |
A responsive table component with composable sub-components. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
tabs |
A set of layered sections of content—known as tab panels—that are displayed one at a time. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
textarea |
Displays a multi-line text input field. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
toast |
A non-blocking notification that displays brief messages to users. Supports auto-dismiss, multiple… | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
toggle |
A two-state button that can be either on or off. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
toggle-group |
A set of two-state buttons that can be toggled on or off. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
tooltip |
A popup that displays contextual information on hover or focus. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
typography |
Styled text elements for headings, paragraphs, and prose content. | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
xyflow |
Signal-based graph editor — Flow, Background, Controls, MiniMap, Handle, NodeWrapper, SimpleEdge | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
#Legend
Each diagnostic code below links to its error-code reference, then to the known limitation entries the adapters' pins cite for it.
No diagnostics were reported — every component compiles cleanly on every adapter.
#Render Conformance (fixture corpus)
The component matrix above is compile-time only. This section answers the question that actually matters: does the construct work, on each adapter's real backend (Go, Ruby, Perl, PHP, Python, Rust), byte-compared against the Hono reference — not just whether it compiles. A construct works either as written (✓) or with a documented /* @client */ comment (✓†, a verified, supported escape — most refusals have one). Fixtures absent from the table below work on every adapter — most as written, some via that documented escape; the headline says how many of each.
The table lists only fixtures that need attention: at least one adapter where nothing works today. Each cell's meaning is in the legend below it; diagnostic codes link to the known limitation they are an instance of, and the reason a code is led by ✗ is in that fixture's detail entry. Escape verification is per adapter, so the same fixture can show ✓† in one column and a code needing attention in another — see escape-coverage.ts.
623 / 640 fixtures work on every adapter (28 of those need a /* @client */ comment on at least one adapter). The 17 below need attention:
✓ Works
✓— as written✓†— with a/* @client */comment (the details below say which diagnostic it silences)
☐ TODO — a registered silent / refusal limitation
≠— compiles, but renders differently from the Hono reference (the linked entry says what renders instead)BF029— a bare diagnostic code: build error, no escape yet
✗ Doesn't work — by design, not a gap
✗BF021— a code led by✗: refused on purpose, no escape will ever be offered (why: in the details below)
The 28 fixtures that need a /* @client */ comment — which diagnostic each silences
conditional-nested-loop-signal-reactivity— 1 adapter, suppressingBF101conditional-nested-loop-static-reactivity— 1 adapter, suppressingBF101every-typeof-predicate— 9 adapters, suppressingBF101fill-unsupported— 9 adapters, suppressingBF101filter-nested-callback-predicate— 7 adapters, suppressingBF101filter-nested-find-predicate— 9 adapters, suppressingBF101filter-typeof-predicate— 9 adapters, suppressingBF021find-typeof-predicate— 9 adapters, suppressingBF101flatmap-typeof-projection— 9 adapters, suppressingBF101loop-row-child-untyped-array-mismatched-keys— 1 adapter, suppressingBF101loop-row-child-untyped-array-nested-field— 1 adapter, suppressingBF101map-array-builder-body— 9 adapters, suppressingBF021map-array-builder-escaping— 9 adapters, suppressingBF021module-const-loop-source-computed— 9 adapters, suppressingBF101opaque-local-accessor-call— 9 adapters, suppressingBF101reduce-right-typeof-body— 9 adapters, suppressingBF101reduce-typeof-body— 9 adapters, suppressingBF101reserved-name-and-internal-twin— 5 adapters, suppressingBF105reserved-name-filter-param-and-internal-prop— 5 adapters, suppressingBF105signal-object-spread-init— 1 adapter, suppressingBF101some-typeof-predicate— 9 adapters, suppressingBF101static-array-from-props— 9 adapters, suppressingBF101static-array-from-props-with-component— 9 adapters, suppressingBF101static-literal-loop-component-in-row— 1 adapter, suppressingBF101static-loop-client-only-nested— 1 adapter, suppressingBF101static-loop-item-boolean-attr— 2 adapters, suppressingBF101static-loop-item-conditional— 2 adapters, suppressingBF101static-nested-loop-ref— 1 adapter, suppressingBF101
#Divergence details
Per adapter, for the 17 fixtures in the table above.
conditional-return-fragment-branch— Conditional return whose default branch is fragment-wrapped now refuses to compile (BF029) instead…blade,erb,go-template,hono,jinja,minijinja,mojolicious,pebble,twig,xslate— refused at build time withBF029—fragment-wrapped-conditional-return-branch-scope
date-method-uncatalogued— Method call on a Date-typed prop refuses with BF021 (no catalogued lowering)blade,erb,go-template,hono,jinja,minijinja,mojolicious,pebble,twig,xslate— refused at build time withBF021—ambient-locale-date-formatting
embedded-prop-member-signal-seed— a signal seeded from an expression over an object prop member renders the computed seed at SSRgo-template— refused at build time withBF101—prop-member-chain-signal-seed
format-date— An authored formatDate(...) call refuses with BF056 on every adapterblade,erb,go-template,hono,jinja,minijinja,mojolicious,pebble,twig,xslate— refused at build time withBF056—authored-format-date-call
jsx-element-prop-array— Array literal wrapping JSX at a non-children prop position refuses with BF021blade,erb,go-template,hono,jinja,minijinja,mojolicious,pebble,twig,xslate— refused at build time withBF021—jsx-wrapped-in-non-children-prop
jsx-element-prop-ternary— Ternary wrapping JSX at a non-children prop position refuses with BF021blade,erb,go-template,hono,jinja,minijinja,mojolicious,pebble,twig,xslate— refused at build time withBF021—jsx-wrapped-in-non-children-prop
loop-row-child-children-attrs— A loop-row child component's JSXchildrenelement keeps its own reactive attrs patched after a…go-template— refused at build time withBF101—loop-row-child-children-attrs-frozen
module-const-arrow-helper— Module-level arrow-valued const helper applied to a prop is referenced by name from the CSR…blade,erb,go-template,jinja,minijinja,mojolicious,pebble,twig,xslate— refused at build time withBF101—module-scope-helper-call
module-function-helper-chain— Module-level function helper calling another module-level function helper is referenced by name…blade,erb,go-template,jinja,minijinja,mojolicious,pebble,twig,xslate— refused at build time withBF101—module-scope-helper-call
module-helper-boolcontext-call— A module-scope helper called from a ternary boolean-test position (#3012)blade,erb,jinja,minijinja,mojolicious,pebble,twig,xslate— refused at build time withBF101—module-scope-helper-callgo-template— refused at build time withBF102—module-scope-helper-call
namespace-import-primitive— A reactive primitive called through a namespace import refuses with BF013blade,erb,go-template,hono,jinja,minijinja,mojolicious,pebble,twig,xslate— refused at build time withBF013—namespace-import-primitive-without-program
optional-object-prop-member-signal-seed— a signal seeded from a member of an optional object prop renders its seed (or the fallback) at SSRgo-template— refused at build time withBF101—prop-member-chain-signal-seed
preamble-cells— keyed .map() row with a preamble-built leaf child — patched in place on same-key update, not frozenblade,erb,go-template,jinja,minijinja,mojolicious,pebble,twig,xslate— refused at build time withBF021— no escape owed (✗): By design: this fixture is the SSR-adopting-hydration + patch-on-update regression pin for #2389. A '/* @client */' escape twin would SSR an empty region, eliminating the adopted DOM node the fixture exists to test patching — collapsing it to the already-covered client-only-loop case with zero new coverage. It is also deliberately kept out of integrations/shared/components (per this file's own docstring) because every DSL integration app's bf build compiles that directory wholesale, so no DSL escape variant is wanted there either. —statement-body-callback
prop-member-length-signal-seed— a signal seeded from.lengthof an array member of an object prop renders its seed at SSRgo-template— refused at build time withBF101—prop-member-chain-signal-seed
ref-mount-attr— A ref mount callback writing an attribute the JSX never renders refuses with BF063blade,erb,go-template,hono,jinja,minijinja,mojolicious,pebble,twig,xslate— refused at build time withBF063—ref-effect-attr-state-ssr
rich-prop-client-read— A Map-typed prop used by client code (even via a method call, since BF021 never sees a handler…blade,erb,go-template,hono,jinja,minijinja,mojolicious,pebble,twig,xslate— refused at build time withBF049—rich-typed-prop-hydration
tag-cloud— flatMap block-body tag list — hydration adoption, in-place leaf patch, add, and removeblade,erb,go-template,jinja,minijinja,mojolicious,pebble,twig,xslate— refused at build time withBF021— no escape owed (✗): By design: this fixture pins hydration ADOPTING pre-existing SSR flatMap leaves (interaction 1) plus in-place patch/add/remove on top of that adopted DOM. A '/* @client */' escape twin would SSR zero leaves, so there would be nothing to adopt — the adoption regression this fixture exists for would be untestable, collapsing it to the already-covered client-only-loop case. Also deliberately kept out of integrations/shared/components (per this file's own docstring) since every DSL integration app's bf build compiles that directory wholesale, so no DSL escape variant is wanted there either. —statement-body-callback
#Construct Support (kind × axis)
Construct-level support, derived from the same fixture-granular conformancePins/renderDivergences as the compat matrix. Pins are attributed to a FIXTURE, not to a construct — a gapped fixture is usually a complex component that incidentally exercises many common kinds/axes alongside the one shape it exists to pin down. Read each cell as a pass/total RATIO over the fixtures that exercise that construct, never as a binary verdict: a construct can show a healthy ratio (e.g. 250/253) with a handful of gapped fixtures — those are real, just narrow. Per-fixture detail for a gap lives in the gaps array (and in the full compat matrix, ui/compat.lock.json); each gap cites the registry limitation (packages/adapter-tests/limitations/
Read this as a ratio, not a binary. conformancePins and renderDivergences are attributed to a whole fixture (a component), not to the individual construct — a gapped fixture is usually a complex component that happens to exercise many common kinds and axes alongside the one shape it exists to pin down. A construct showing 137/138 has exactly one gapped fixture contributing to it, not 137 broken cases. A construct with total: 0 (currently just regex) has no fixture exercising it yet.
Each construct name links to a conformance fixture exercising it — a small self-contained component showing the case in real code — and the Example column is that fixture's description. Constructs with no covering fixture yet link to where they're registered in the compiler instead.
#By kind
#By axis
| Construct | Example | hono | blade | erb | go-template | jinja | minijinja | mojolicious | pebble | twig | xslate |
|---|---|---|---|---|---|---|---|---|---|---|---|
array-method:at |
items.at().at() with no argument returns the first element |
2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 |
array-method:concat |
left.concat(right).concat(other) merges two arrays in order |
2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 |
array-method:endsWith |
value.endsWith(suffix).endsWith(suffix) renders the matching branch |
2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 |
array-method:flat |
rows.flat().flat() flattens one level |
4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 |
array-method:includes |
items.includes(target).includes(x) on an array prop renders the matching branch |
12/12 | 12/12 | 12/12 | 12/12 | 12/12 | 12/12 | 12/12 | 12/12 | 12/12 | 12/12 |
array-method:indexOf |
items.indexOf(target).indexOf(x) returns the 0-based position |
1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 |
array-method:join |
items.join().join() with no argument joins with the default comma |
44/44 | 42/44 (array-fill, off-subset-callback-body) |
42/44 (array-fill, off-subset-callback-body) |
42/44 (array-fill, off-subset-callback-body) |
42/44 (array-fill, off-subset-callback-body) |
42/44 (array-fill, off-subset-callback-body) |
42/44 (array-fill, off-subset-callback-body) |
42/44 (array-fill, off-subset-callback-body) |
42/44 (array-fill, off-subset-callback-body) |
42/44 (array-fill, off-subset-callback-body) |
array-method:lastIndexOf |
items.lastIndexOf(target).lastIndexOf(x) returns the position of the last match |
1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 |
array-method:padEnd |
value.padEnd(5, '.').padEnd(target, pad) right-pads to the target width |
1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 |
array-method:padStart |
value.padStart(5, '0').padStart(target, pad) left-pads to the target width |
1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 |
array-method:repeat |
value.repeat(3).repeat(n) concatenates the string n times |
1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 |
array-method:replace |
value.replace('o', '0').replace(old, new) swaps the first occurrence |
1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 |
array-method:replaceAll |
path.replaceAll('/', ' > ').replaceAll replaces every occurrence, not just the first |
1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 |
array-method:reverse |
items.reverse().reverse() emits the array in reverse order |
1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 |
array-method:slice |
items.slice().slice() with no argument copies the whole array |
5/5 | 4/5 (statement-body-callback) |
4/5 (statement-body-callback) |
4/5 (statement-body-callback) |
4/5 (statement-body-callback) |
4/5 (statement-body-callback) |
4/5 (statement-body-callback) |
4/5 (statement-body-callback) |
4/5 (statement-body-callback) |
4/5 (statement-body-callback) |
array-method:split |
value.split(',').split(sep) splits a string into an array of substrings |
2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 |
array-method:startsWith |
value.startsWith(prefix).startsWith(prefix) renders the matching branch |
3/3 | 3/3 | 3/3 | 3/3 | 3/3 | 3/3 | 3/3 | 3/3 | 3/3 | 3/3 |
array-method:toFixed |
price.toFixed(2).toFixed(2) formats a numeric prop with rounding and zero-padding |
3/3 | 3/3 | 3/3 | 3/3 | 3/3 | 3/3 | 3/3 | 3/3 | 3/3 | 3/3 |
array-method:toLowerCase |
value.toLowerCase().toLowerCase() lowercases the receiver string |
4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 |
array-method:toReversed |
items.toReversed().toReversed() emits the array in reverse order |
1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 |
array-method:toUpperCase |
value.toUpperCase().toUpperCase() uppercases the receiver string |
6/6 | 4/6 (module-scope-helper-call, statement-body-callback) |
4/6 (module-scope-helper-call, statement-body-callback) |
4/6 (module-scope-helper-call, statement-body-callback) |
4/6 (module-scope-helper-call, statement-body-callback) |
4/6 (module-scope-helper-call, statement-body-callback) |
4/6 (module-scope-helper-call, statement-body-callback) |
4/6 (module-scope-helper-call, statement-body-callback) |
4/6 (module-scope-helper-call, statement-body-callback) |
4/6 (module-scope-helper-call, statement-body-callback) |
array-method:trim |
value.trim().trim() strips leading and trailing whitespace |
2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 |
array-method:trimEnd |
value.trimEnd().trimStart() and .trimEnd() strip only their own side |
1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 |
array-method:trimStart |
value.trimStart().trimStart() and .trimEnd() strip only their own side |
1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 |
binary:!= |
Nullish coalescing with JSX fallback in JSX child position | 3/3 | 3/3 | 3/3 | 3/3 | 3/3 | 3/3 | 3/3 | 3/3 | 3/3 | 3/3 |
binary:!== |
it.id !== props.hiddenId.filter() predicate reading props.x directly on a bare-props-form component (#2879) |
14/14 | 14/14 | 14/14 | 14/14 | 14/14 | 14/14 | 14/14 | 14/14 | 14/14 | 14/14 |
binary:% |
row.n % 2.filter() predicate using % (modulo) (#2873) |
1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 |
binary:* |
value * 2Fractional number-array elements work in row comparisons and arithmetic |
33/33 | 33/33 | 33/33 | 33/33 | 33/33 | 33/33 | 33/33 | 33/33 | 33/33 | 33/33 |
binary:+ |
value + valueNormalized array and object entries row values retain scalar string evidence |
51/51 | 50/51 (computed-const-loop-source) |
50/51 (computed-const-loop-source) |
47/51 (computed-const-loop-source, conditional-nested-const-loop-source, prop-member-chain-signal-seed) |
50/51 (computed-const-loop-source) |
50/51 (computed-const-loop-source) |
50/51 (computed-const-loop-source) |
50/51 (computed-const-loop-source) |
50/51 (computed-const-loop-source) |
50/51 (computed-const-loop-source) |
binary:- |
a - b.sort((a,b) => a - b) sorts a primitive array by item value |
18/18 | 18/18 | 18/18 | 18/18 | 18/18 | 18/18 | 18/18 | 18/18 | 18/18 | 18/18 |
binary:/ |
value / 8A memo dividing a destructured number prop by an integer literal renders the JavaScript quotient |
10/10 | 10/10 | 10/10 | 10/10 | 10/10 | 10/10 | 10/10 | 10/10 | 10/10 | 10/10 |
binary:< |
props.value < 0Attribute arithmetic keeps JavaScript number spelling for negative, integral, ternary and memo values |
2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 | 2/2 |
binary:=== |
t === andA filter capture named like a template keyword reads the enclosing value |
82/82 | 74/82 (nested-callback-in-filter-predicate, off-subset-callback-body, statement-body-callback) |
75/82 (nested-callback-in-filter-predicate, off-subset-callback-body, statement-body-callback) |
71/82 (conditional-nested-const-loop-source, loop-row-child-children-attrs-frozen, nested-callback-in-filter-predicate, off-subset-callback-body, statement-body-callback) |
74/82 (nested-callback-in-filter-predicate, off-subset-callback-body, statement-body-callback) |
74/82 (nested-callback-in-filter-predicate, off-subset-callback-body, statement-body-callback) |
75/82 (nested-callback-in-filter-predicate, off-subset-callback-body, statement-body-callback) |
74/82 (nested-callback-in-filter-predicate, off-subset-callback-body, statement-body-callback) |
74/82 (nested-callback-in-filter-predicate, off-subset-callback-body, statement-body-callback) |
74/82 (nested-callback-in-filter-predicate, off-subset-callback-body, statement-body-callback) |
binary:> |
count() > 0Top-level ternary return preserves conditional (#968) |
24/24 | 24/24 | 24/24 | 24/24 | 24/24 | 24/24 | 24/24 | 24/24 | 24/24 | 24/24 |
binary:>= |
n() >= 10Comparison operators (>=, ===, !==) as ternary conditions |
1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 |
literal:boolean |
trueStatic boolean attributes: bare, ={true}, and ={false} omission |
107/108 (jsx-wrapped-in-non-children-prop) |
106/108 (jsx-wrapped-in-non-children-prop, statement-body-callback) |
106/108 (jsx-wrapped-in-non-children-prop, statement-body-callback) |
103/108 (conditional-nested-const-loop-source, derived-object-literal-signal, jsx-wrapped-in-non-children-prop, statement-body-callback) |
106/108 (jsx-wrapped-in-non-children-prop, statement-body-callback) |
106/108 (jsx-wrapped-in-non-children-prop, statement-body-callback) |
104/108 (boolean-in-static-loop-item, jsx-wrapped-in-non-children-prop, statement-body-callback) |
106/108 (jsx-wrapped-in-non-children-prop, statement-body-callback) |
106/108 (jsx-wrapped-in-non-children-prop, statement-body-callback) |
104/108 (boolean-in-static-loop-item, jsx-wrapped-in-non-children-prop, statement-body-callback) |
literal:null |
Falsy-zero {n && jsx} renders nothing (documented React divergence) |
49/49 | 49/49 | 49/49 | 47/49 (conditional-nested-const-loop-source) |
49/49 | 49/49 | 48/49 (boolean-in-static-loop-item) |
49/49 | 49/49 | 48/49 (boolean-in-static-loop-item) |
literal:number |
2Inline SVG: viewBox casing and camelCase → kebab-case presentation attrs |
215/216 (fragment-wrapped-conditional-return-branch-scope) |
206/216 (array-fill, fragment-wrapped-conditional-return-branch-scope, module-scope-helper-call, off-subset-callback-body, opaque-local-accessor-call, statement-body-callback) |
206/216 (array-fill, fragment-wrapped-conditional-return-branch-scope, module-scope-helper-call, off-subset-callback-body, opaque-local-accessor-call, statement-body-callback) |
202/216 (array-fill, client-only-loop-in-static-loop, fragment-wrapped-conditional-return-branch-scope, module-scope-helper-call, off-subset-callback-body, opaque-local-accessor-call, prop-member-chain-signal-seed, signal-read-in-nested-static-loop, statement-body-callback, untyped-loop-array-no-row-struct) |
206/216 (array-fill, fragment-wrapped-conditional-return-branch-scope, module-scope-helper-call, off-subset-callback-body, opaque-local-accessor-call, statement-body-callback) |
206/216 (array-fill, fragment-wrapped-conditional-return-branch-scope, module-scope-helper-call, off-subset-callback-body, opaque-local-accessor-call, statement-body-callback) |
204/216 (array-fill, boolean-in-static-loop-item, fragment-wrapped-conditional-return-branch-scope, module-scope-helper-call, off-subset-callback-body, opaque-local-accessor-call, statement-body-callback) |
206/216 (array-fill, fragment-wrapped-conditional-return-branch-scope, module-scope-helper-call, off-subset-callback-body, opaque-local-accessor-call, statement-body-callback) |
206/216 (array-fill, fragment-wrapped-conditional-return-branch-scope, module-scope-helper-call, off-subset-callback-body, opaque-local-accessor-call, statement-body-callback) |
204/216 (array-fill, boolean-in-static-loop-item, fragment-wrapped-conditional-return-branch-scope, module-scope-helper-call, off-subset-callback-body, opaque-local-accessor-call, statement-body-callback) |
literal:string |
'a'A function-scope string-literal const compared in a ternary test renders the branch it selects |
321/322 (authored-format-date-call) |
313/322 (array-fill, authored-format-date-call, computed-const-loop-source, off-subset-callback-body, statement-body-callback) |
313/322 (array-fill, authored-format-date-call, computed-const-loop-source, off-subset-callback-body, statement-body-callback) |
305/322 (array-fill, authored-format-date-call, computed-const-loop-source, conditional-nested-const-loop-source, derived-object-literal-signal, loop-row-child-children-attrs-frozen, off-subset-callback-body, prop-member-chain-signal-seed, statement-body-callback, static-literal-loop-component-row, untyped-loop-array-no-row-struct) |
313/322 (array-fill, authored-format-date-call, computed-const-loop-source, off-subset-callback-body, statement-body-callback) |
313/322 (array-fill, authored-format-date-call, computed-const-loop-source, off-subset-callback-body, statement-body-callback) |
311/322 (array-fill, authored-format-date-call, boolean-in-static-loop-item, computed-const-loop-source, off-subset-callback-body, statement-body-callback) |
313/322 (array-fill, authored-format-date-call, computed-const-loop-source, off-subset-callback-body, statement-body-callback) |
313/322 (array-fill, authored-format-date-call, computed-const-loop-source, off-subset-callback-body, statement-body-callback) |
311/322 (array-fill, authored-format-date-call, boolean-in-static-loop-item, computed-const-loop-source, off-subset-callback-body, statement-body-callback) |
logical:&& |
ready() && visible()Chained a() && b() && |
12/12 | 12/12 | 12/12 | 12/12 | 12/12 | 12/12 | 12/12 | 12/12 | 12/12 | 12/12 |
logical:?? |
props.c ?? props.a?? keeps a present empty array instead of taking the fallback |
73/73 | 71/73 (computed-const-loop-source, statement-body-callback) |
71/73 (computed-const-loop-source, statement-body-callback) |
70/73 (computed-const-loop-source, prop-member-chain-signal-seed, statement-body-callback) |
71/73 (computed-const-loop-source, statement-body-callback) |
71/73 (computed-const-loop-source, statement-body-callback) |
71/73 (computed-const-loop-source, statement-body-callback) |
71/73 (computed-const-loop-source, statement-body-callback) |
71/73 (computed-const-loop-source, statement-body-callback) |
71/73 (computed-const-loop-source, statement-body-callback) |
logical:|| |
row.done || props.highlightLoop row preamble computes a conditional-condition value from item + prop |
23/23 | 23/23 | 23/23 | 23/23 | 23/23 | 23/23 | 23/23 | 23/23 | 23/23 | 23/23 |
lowering:date |
Date-typed prop: toISOString() and getUTCFullYear() render via the catalogued date helper | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 |
lowering:queryHref |
queryHref(base, { tag, page }) with object-literal shorthand still rewrites both props | 5/5 | 5/5 | 5/5 | 5/5 | 5/5 | 5/5 | 5/5 | 5/5 | 5/5 | 5/5 |
lowering:toLocaleDateString |
toLocaleDateString dateStyle options lower to name-token patterns + shipped name tables | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 | 4/4 |
member:computed |
meta()?.['active']A signal seeded from an optional nested object retains its nullable field type |
15/15 | 15/15 | 15/15 | 15/15 | 15/15 | 15/15 | 15/15 | 15/15 | 15/15 | 15/15 |
member:optional |
props.items?.lengthA bare optional-chained .length over an absent array renders empty |
28/28 | 28/28 | 28/28 | 27/28 (prop-member-chain-signal-seed) |
28/28 | 28/28 | 28/28 | 28/28 | 28/28 | 28/28 |
sort-direction:asc |
.sort((a,b) => a - b) sorts a primitive array by item value | 7/7 | 7/7 | 7/7 | 7/7 | 7/7 | 7/7 | 7/7 | 7/7 | 7/7 | 7/7 |
sort-direction:desc |
.sort((a,b) => b.f - a.f) sorts by field, descending | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 |
sort-key:auto |
.toSorted((a,b) => a.rank > b.rank ? 1 : -1) sorts ascending by rank | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 |
sort-key:numeric |
.sort((a,b) => a - b) sorts a primitive array by item value | 7/7 | 7/7 | 7/7 | 7/7 | 7/7 | 7/7 | 7/7 | 7/7 | 7/7 | 7/7 |
sort-key:string |
.sort((a,b) => a.price - b.price || a.name.localeCompare(b.name)) sorts by price, then name | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 |
sort-target:field |
.sort((a,b) => a.f - b.f) sorts by field, ascending | 7/7 | 7/7 | 7/7 | 7/7 | 7/7 | 7/7 | 7/7 | 7/7 | 7/7 | 7/7 |
sort-target:self |
.sort((a,b) => a - b) sorts a primitive array by item value | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 |
sort:multi-key |
.sort((a,b) => a.price - b.price || a.name.localeCompare(b.name)) sorts by price, then name | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 | 1/1 |
unary:! |
!posts()createQuery with initial from an absent optional prop SSR-renders the skeleton and sends on mount |
36/36 | 34/36 (nested-callback-in-filter-predicate, statement-body-callback) |
35/36 (statement-body-callback) |
34/36 (nested-callback-in-filter-predicate, statement-body-callback) |
34/36 (nested-callback-in-filter-predicate, statement-body-callback) |
34/36 (nested-callback-in-filter-predicate, statement-body-callback) |
35/36 (statement-body-callback) |
34/36 (nested-callback-in-filter-predicate, statement-body-callback) |
34/36 (nested-callback-in-filter-predicate, statement-body-callback) |
34/36 (nested-callback-in-filter-predicate, statement-body-callback) |
unary:- |
-1.at(-1) returns the last element |
14/14 | 14/14 | 14/14 | 14/14 | 14/14 | 14/14 | 14/14 | 14/14 | 14/14 | 14/14 |
#Known Limitations
Every gap on this page is an instance of a registered limitation — one file per entry under packages/adapter-tests/limitations/, the source of truth for what is limited. An entry states the input shape (given), what the Hono reference renders for it (expected), what the affected adapters do instead (actual), and the minimal fixtures that reproduce it; which adapters are affected is derived from the adapter pins, render divergences and real-browser e2e quarantine rows citing the entry. The kind is the compatibility policy's classifier: a silent divergence is a defect, a loud refusal is an escapable capability gap, and by design is a permanent position with its reason stated.
#Ambient-locale Date formatting
ambient-locale-date-formatting · by design — permanent, no fix planned · source
| given | a Date-typed prop formatted with zero-argument toLocaleDateString() (no explicit locale or options) |
| expected | the date renders formatted for the server process default locale |
| actual | the compiler refuses with BF021 (by design) |
| reason | The zero-argument form resolves against the runtime environment ambient locale (ICU/CLDR data, `LC_*`), which is not knowable at build time and differs across every backend language and between the server and the browser. Any lowering would be a silent SSR/CSR divergence; a Hono carve-out was tried and rejected because hydration re-evaluates the expression against a JSON-de-riched receiver. The literal-locale and named-timezone forms are lowered instead. |
| diagnostic | BF021 |
| adapters | hono, blade, erb, go-template, jinja, minijinja, mojolicious, pebble, twig, xslate |
| fixtures | date-method-uncatalogued |
#Array.prototype.fill in a template expression
array-fill · loud refusal — a capability gap, escapable · source
| given | a template expression calling .fill(value) on an array |
| expected | the filled array renders in the server HTML |
| actual | the compiler refuses with BF101 |
| diagnostic | BF101 |
| adapters | blade, erb, go-template, jinja, minijinja, mojolicious, pebble, twig, xslate |
| fixtures | fill-unsupported |
#Authored call to formatDate
authored-format-date-call · loud refusal — a capability gap, escapable · source
| given | a template-position call to formatDate imported by name from @barefootjs/client (or its /runtime subpath), e.g. {formatDate(date, pattern)} |
| expected | a compile-time refusal pointing at date.toLocaleDateString(locale, { timeZone, ... }) with literal options, or /* @client */ to defer the read |
| actual | the compiler refuses with BF056 |
| diagnostic | BF056 |
| adapters | hono, blade, erb, go-template, jinja, minijinja, mojolicious, pebble, twig, xslate |
| fixtures | format-date |
#Boolean field in a static loop item
boolean-in-static-loop-item · loud refusal — a capability gap, escapable · source
| given | a static array .map() whose item objects carry a boolean field, read by a conditional or a boolean attribute |
| expected | each row renders in the server HTML with the boolean applied per item |
| actual | the compiler refuses with BF101 |
| diagnostic | BF101 |
| adapters | mojolicious, xslate |
| fixtures | static-loop-item-boolean-attr, static-loop-item-conditional |
#Client-only nested loop inside a static loop row
client-only-loop-in-static-loop · loud refusal — a capability gap, escapable · source
| given | a static (non-signal) array .map() whose row contains a /* @client */ nested .map() |
| expected | the outer rows render in the server HTML, each with an empty inner loop host for the client to fill |
| actual | the compiler refuses with BF101 |
| diagnostic | BF101 |
| adapters | go-template |
| fixtures | static-loop-client-only-nested |
#Computed component-scope const as a loop source
computed-const-loop-source · loud refusal — a capability gap, escapable · source
| given | a .map() whose array is a component- or module-scope const computed from props or a function call |
| expected | the rows render in the server HTML |
| actual | the compiler refuses with BF101 |
| diagnostic | BF101 |
| adapters | blade, erb, go-template, jinja, minijinja, mojolicious, pebble, twig, xslate |
| fixtures | module-const-loop-source-computed, static-array-from-props, static-array-from-props-with-component |
#Const string arrays in conditional nested loops
conditional-nested-const-loop-source · loud refusal — a capability gap, escapable · source
| given | nested .map() rows inside a reactive conditional branch, with an inner module-scope const string-array source and a const or signal outer source |
| expected | both levels of rows render in the server HTML |
| actual | the compiler refuses with BF101 |
| diagnostic | BF101 |
| adapters | go-template |
| fixtures | conditional-nested-loop-signal-reactivity, conditional-nested-loop-static-reactivity |
#Derived object-literal signal initializer
derived-object-literal-signal · loud refusal — a capability gap, escapable · source
| given | a createSignal / createMemo whose initial value is an object literal referencing a live prop (with or without a spread) |
| expected | field reads on the signal render the values the caller passed |
| actual | the compiler refuses with BF101 |
| diagnostic | BF101 |
| adapters | go-template |
| fixtures | signal-object-spread-init |
#Fragment-wrapped branch of a conditional return is never hydrated
fragment-wrapped-conditional-return-branch-scope · loud refusal — a capability gap, escapable · source
| given | a component with an early-return branch whose other branch is wrapped in a fragment (if (x) return <a/>; return <><button/></>) |
| expected | the rendered branch root carries the component scope id on every render path, so its events bind |
| actual | the compiler refuses with BF029 |
| diagnostic | BF029 |
| adapters | hono, blade, erb, go-template, jinja, minijinja, mojolicious, pebble, twig, xslate |
| fixtures | conditional-return-fragment-branch |
#JSX wrapped in a ternary or array at a non-children prop
jsx-wrapped-in-non-children-prop · by design — permanent, no fix planned · source
| given | a component prop other than children whose value is a ternary or array literal wrapping JSX elements (header={cond ? <a/> : <b/>}) |
| expected | the chosen elements render inside the child in the server HTML |
| actual | the compiler refuses with BF021 (by design) |
| reason | The shape is refused in the shared JSX-to-IR phase, identically on every adapter including the reference: the classifier has no structured node for it, and the only alternative was splicing raw JSX source text into the emitted client JS untyped. Passing the elements as `children`, or lifting the ternary into a single JSX element, is the supported form. |
| diagnostic | BF021 |
| adapters | hono, blade, erb, go-template, jinja, minijinja, mojolicious, pebble, twig, xslate |
| fixtures | jsx-element-prop-array, jsx-element-prop-ternary |
#A loop-row child component's forwarded JSX children can't reach an outer signal/memo at SSR
loop-row-child-children-attrs-frozen · loud refusal — a capability gap, escapable · source
| given | a static .map() loop row that calls a child component passing a JSX element as children, where that element reads an OUTER signal/memo — not just the row's own item (e.g. <Chip><a href={active() === item ? …}>…</a></Chip> inside items.map(item => …), module- or function-scope array alike) |
| expected | the row's markup — including the forwarded child element, evaluated against the signal's initial value — renders at SSR the same way a non-loop reference to the same signal does |
| actual | the compiler refuses with BF101 |
| diagnostic | BF101 |
| adapters | go-template |
| fixtures | loop-row-child-children-attrs |
#Module-scope helper called from a template position
module-scope-helper-call · loud refusal — a capability gap, escapable · source
| given | a template expression calling a module-scope helper by bare name (an arrow-valued const, a function declaration, or a chain of them), in text or boolean-test position |
| expected | the helper runs at render time and its result renders in the server HTML |
| actual | the compiler refuses with BF101 / BF102 |
| diagnostic | BF101, BF102 |
| adapters | blade, erb, go-template, jinja, minijinja, mojolicious, pebble, twig, xslate |
| fixtures | module-const-arrow-helper, module-function-helper-chain, module-helper-boolcontext-call |
#Reactive primitive called through a namespace import
namespace-import-primitive-without-program · by design — permanent, no fix planned · source
| given | a reactive primitive invoked through a namespace import (import * as bf, bf.createSignal(...)) in a compile with no shared ts.Program |
| expected | the signal is declared and the component hydrates |
| actual | the compiler refuses with BF013 (by design) |
| reason | Without a type checker the analyzer fast path matches only a bare identifier callee, so the declaration would be silently dropped and every reference would throw at hydrate. The loud refusal is the permanent answer for checker-less compiles; a compile that supplies a shared program (the Vite plugin does) resolves the primitive normally and never reaches it. Importing the primitive by name is the supported form. |
| diagnostic | BF013 |
| adapters | hono, blade, erb, go-template, jinja, minijinja, mojolicious, pebble, twig, xslate |
| fixtures | namespace-import-primitive |
#Nested higher-order callback inside a filter predicate
nested-callback-in-filter-predicate · loud refusal — a capability gap, escapable · source
| given | a .filter() predicate whose body calls .some() / .find() with its own arrow callback |
| expected | the filtered rows render in the server HTML with the nested predicate applied |
| actual | the compiler refuses with BF101 |
| diagnostic | BF101 |
| adapters | blade, erb, go-template, jinja, minijinja, mojolicious, pebble, twig, xslate |
| fixtures | filter-nested-callback-predicate, filter-nested-find-predicate |
#Off-subset expression in a collection-method callback
off-subset-callback-body · loud refusal — a capability gap, escapable · source
| given | a .filter() / .find() / .some() / .every() / .reduce() / .flatMap() callback whose body uses typeof |
| expected | the callback runs and the result renders in the server HTML |
| actual | the compiler refuses with BF021 / BF101 |
| diagnostic | BF021, BF101 |
| adapters | blade, erb, go-template, jinja, minijinja, mojolicious, pebble, twig, xslate |
| fixtures | every-typeof-predicate, filter-typeof-predicate, find-typeof-predicate, flatmap-typeof-projection, reduce-right-typeof-body, reduce-typeof-body, some-typeof-predicate |
#Opaque local accessor invoked from a template position
opaque-local-accessor-call · loud refusal — a capability gap, escapable · source
| given | a component-body const bound to the result of a call the compiler cannot evaluate (a helper returning a function, or an accessor returned by a library), invoked with () in text position |
| expected | the accessor runs at render time and its result renders in the server HTML |
| actual | the compiler refuses with BF101 |
| diagnostic | BF101 |
| adapters | blade, erb, go-template, jinja, minijinja, mojolicious, pebble, twig, xslate |
| fixtures | opaque-local-accessor-call |
#Signal seeded through a prop member chain that is not a plain path to an object field
prop-member-chain-signal-seed · loud refusal — a capability gap, escapable · source
| given | a createSignal whose initial value reads a member chain rooted at a prop through an optional object-typed prop ({ initial }: { initial?: State }, createSignal(initial?.label ?? 'none')), through an array or primitive member (createSignal(initial.items.length)), or inside a larger expression (createSignal(initial.count + 1)), rendered as text |
| expected | the server HTML renders the seeded value: the member the caller passed (the fallback when the prop is absent), the array's length, or the computed expression |
| actual | the compiler refuses with BF101 |
| diagnostic | BF101 |
| adapters | go-template |
| fixtures | embedded-prop-member-signal-seed, optional-object-prop-member-signal-seed, prop-member-length-signal-seed |
#Attribute written only by a ref callback never reaches SSR
ref-effect-attr-state-ssr · loud refusal — a capability gap, escapable · source
| given | an element whose ref callback unconditionally writes an attribute on mount — el.setAttribute('<name>', …) or el.dataset.<key> = …, at the top level of the ref body or of a createEffect / onMount directly inside it — that the element's own JSX never renders |
| expected | the server HTML and the hydrated DOM carry the same attribute value |
| actual | the compiler refuses with BF063 |
| diagnostic | BF063 |
| adapters | hono, blade, erb, go-template, jinja, minijinja, mojolicious, pebble, twig, xslate |
| fixtures | ref-mount-attr |
#Reserved name and the internal name it is renamed to
reserved-name-collides-with-internal-name · loud refusal — a capability gap, escapable · source
| given | a component that uses both loop and __bf_loop as names visible at the same point |
| expected | each name renders its own value |
| actual | the compiler refuses with BF105 |
| diagnostic | BF105 |
| adapters | blade, jinja, minijinja, pebble, twig |
| fixtures | reserved-name-and-internal-twin, reserved-name-filter-param-and-internal-prop |
#Rich-typed prop read by client code
rich-typed-prop-hydration · by design — permanent, no fix planned · source
| given | a prop typed as a JSON-unsafe host type (Map, Set, WeakMap, WeakSet, URLSearchParams, RegExp, Promise, Error, Symbol, BigInt, Function) that the component own client code reads |
| expected | the client handler sees the same value the server rendered with |
| actual | the compiler refuses with BF049 (by design) |
| reason | Props cross the `bf-p` hydration boundary as `JSON.stringify` data with no type-aware revival, so these types lose their contents (`Map` / `Set` serialize to `{}`, `BigInt` throws, `Promise` / `Function` cannot cross a serialization boundary at all). A typed revival envelope was considered and rejected as a nine-adapter protocol change that still could not carry every listed type. The sound path is to pass a JSON-safe value and rebuild the rich value client-side. |
| diagnostic | BF049 |
| adapters | hono, blade, erb, go-template, jinja, minijinja, mojolicious, pebble, twig, xslate |
| fixtures | rich-prop-client-read |
#Signal read inside a nested static loop row
signal-read-in-nested-static-loop · loud refusal — a capability gap, escapable · source
| given | a static (non-signal) array .map() whose row contains a nested .map() reading a signal or memo getter |
| expected | both loop levels render in the server HTML with the signal initial value |
| actual | the compiler refuses with BF101 |
| diagnostic | BF101 |
| adapters | go-template |
| fixtures | static-nested-loop-ref |
#Statement-carrying .map() / .flatMap() callback body
statement-body-callback · loud refusal — a capability gap, escapable · source
| given | a .map() or .flatMap() callback whose body is a block with statements (an imperative array builder, a const preamble feeding a leaf, an early return) |
| expected | the callback body runs and every row renders in the server HTML |
| actual | the compiler refuses with BF021 |
| diagnostic | BF021 |
| adapters | blade, erb, go-template, jinja, minijinja, mojolicious, pebble, twig, xslate |
| fixtures | map-array-builder-body, map-array-builder-escaping, preamble-cells, tag-cloud |
#Child component in a row of a loop over a static literal array
static-literal-loop-component-row · loud refusal — a capability gap, escapable · source
| given | a .map() over a module- or function-scope const initialized with a static array literal, whose row renders a child component anywhere other than as the row root (inside an element, a conditional branch or a nested loop) |
| expected | every row renders in the server HTML |
| actual | the compiler refuses with BF101 |
| diagnostic | BF101 |
| adapters | go-template |
| fixtures | static-literal-loop-component-in-row |
#Untyped object-literal loop array with no single row shape
untyped-loop-array-no-row-struct · loud refusal — a capability gap, escapable · source
| given | a .map() whose rows call a child component, over an unannotated module-scope const array of object literals whose rows do not share one set of fields with one plain type each: for example the rows have different keys, or a field is a nested object, a null, a non-identifier key, an empty nested array, or a value whose type differs between rows |
| expected | the rows render in the server HTML |
| actual | the compiler refuses with BF101 |
| diagnostic | BF101 |
| adapters | go-template |
| fixtures | loop-row-child-untyped-array-mismatched-keys, loop-row-child-untyped-array-nested-field |
#Data Source
This table is generated from the committed ui/compat.lock.json, regenerated with bun run compat:lock and drift-checked in CI. Render-level divergences are declared per adapter in packages/adapter-*/src/render-divergences.ts (each adapter's conformance suite derives its skip list from the same declaration, so this page and the tests cannot drift apart). Every pin and divergence cites a registry entry under packages/adapter-tests/limitations/; the join is tested both ways (limitations-join.test.ts), so a link on this page cannot point at an entry that does not exist, and an entry cannot outlive its last pin.
Component and fixture descriptions are computed into the same lock at regen time (component-docs.ts) — component copy is taken from the public component page's own description (falling back to the ui/registry.json catalogue one-liner, then the component's JSDoc tagline), and the component name links to that page when one exists; fixture descriptions come from the corpus itself. All of it is drift-checked with everything else and can't go stale independently.
The construct-support section above is generated from the committed ui/support-matrix.lock.json, regenerated with bun run support-matrix:lock and drift-checked in CI alongside the component matrix. Each construct's fixture link and example description are computed the same way — the exemplar is mechanically chosen as the most narrowly targeted covering fixture (construct-examples.ts), and definition-site fallback links come from a TS AST walk over the compiler's construct catalogues (construct-source-links.ts) — so they are regenerated and drift-checked on the same schedule: a link can't silently go stale, because moving the code it points at changes what the next regen commits, and CI fails if that regen was forgotten.