Tabs without `defaultValue`: with the panels before the list, the server selects the first tab and renders no panel

Open
#533 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
35/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Active
Tech stack
typescript
Domain
frontend, web-dev

Research direction

The Tabs server render order lives in the core tabs behavior exercised by checkServer from @ui-sdk/core/testing, and the Solid adapter @ui-sdk/solid/tabs shows the rendered output. Start by reproducing the panels-first case with checkServer(tabs, {}, panelsFirst) and compare it with the list-first markup. Done means the server HTML selects a tab and renders its panel regardless of order, or the docs state that server-rendered Tabs need value or defaultValue.

Written by the indexing model from the issue text.

Description

What happens

Without value or defaultValue, Tabs selects "the first enabled tab". On the server, that
depends on render order. With the list before the panels the server HTML is right (first tab
selected, its panel rendered). With the panels before the list (a bottom tab bar, a phone layout)
the panels render before any tab has registered, so none of them is selected: the server HTML has
the first tab selected and no panel. The client renders the panel at hydration (no hydration
warning), so the page shows an empty content area until the bundle runs.

checkServer already says why: it reports both orders without a default as reading options.

Minimal reproduction

Core only, no framework, with @ui-sdk/core/testing:

import { tabs } from '@ui-sdk/core/tabs'
import { checkServer } from '@ui-sdk/core/testing'

const panelsFirst =
	'<root><panel value="a">PA</panel><panel value="b">PB</panel><list><tab value="a">A</tab><tab value="b">B</tab></list></root>'

checkServer(tabs, {}, panelsFirst)
// throws: checkServer found 4 problems:
// - `panel` "a" reads option `value`
// - `panel` "b" reads option `value`
// - `tab` "a" reads option `value`
// - `tab` "b" reads option `value`

checkServer(tabs, { defaultValue: 'b' }, panelsFirst)
// <div data-slot="tabs"><div data-slot="tabs-panel" role="tabpanel" … data-selected>PB</div>
//   <div data-slot="tabs-list" role="tablist">…aria-selected="true" data-selected>B</button></div></div>

The list-first markup without a default throws the same four problems. Rendered through
@ui-sdk/solid/tabs on the server (Solid 2 renderToString, then hydrate), panels first and no
default:

<div data-slot="tabs">
	<div data-slot="tabs-list" role="tablist" aria-label="Sections">
		<button
			data-slot="tabs-tab"
			role="tab"
			…
			aria-selected="true"
			data-selected
		>
			A
		</button>
		<button data-slot="tabs-tab" role="tab" … aria-selected="false">B</button>
		<span data-slot="tabs-indicator" role="presentation"></span>
	</div>
</div>

No tabpanel at all; after hydration panel A is there. Reproduced twice with checkServer and
on two server renders.

Versions
  • @ui-sdk/core 0.1.0-beta.1, @ui-sdk/solid 0.1.0-beta.1, Solid 2.0.0-rc.21
Expected behavior

Either the server resolves "the first enabled tab" independent of render order (the panels could
be rendered once the tabs are known, or the first panel could stand in), or the docs say that a
server-rendered Tabs needs value or defaultValue, as Server Rendering already says for ids
("render order matters"). Today only checkServer hints at it.


Reproduced on @ui-sdk/core / @ui-sdk/solid 0.1.0-beta.2.

Dominant language
TypeScript
Stars
4.4k
Forks
122
Avg merge
3h 12m
Merged PRs (30d)
437

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from vercel-labs/ui

All issues in vercel-labs/ui

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.