Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/entry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { App } from './app.tsx'

const rootElement = document.getElementById('root') ?? document.body
if (rootElement.childNodes.length > 0) {
// Remix alpha.3 auto-hydrates non-empty containers. We render from scratch.
// Remix UI auto-hydrates non-empty containers. We render from scratch.
rootElement.replaceChildren()
}
createRoot(rootElement).render(<App />)
3 changes: 3 additions & 0 deletions client/routes/admin-agents.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type Handle } from 'remix/ui'
import checkbox from 'remix/ui/checkbox'
import { createNotifications } from '#client/notifications.tsx'
import {
colors,
Expand Down Expand Up @@ -611,6 +612,7 @@ export function AdminAgentsRoute(handle: Handle) {
<input
type="checkbox"
checked={draft.isActive}
mix={[...checkbox()]}
on={{
change: (event) => handleCheckboxInput('isActive', event),
}}
Expand All @@ -624,6 +626,7 @@ export function AdminAgentsRoute(handle: Handle) {
<input
type="checkbox"
checked={draft.makeDefault}
mix={[...checkbox()]}
on={{
change: (event) =>
handleCheckboxInput('makeDefault', event),
Expand Down
2 changes: 2 additions & 0 deletions client/routes/login.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type Handle } from 'remix/ui'
import checkbox from 'remix/ui/checkbox'
import { buildAuthLink } from '#client/auth-links.ts'
import { navigate } from '#client/client-router.tsx'
import { fetchSessionInfo, type SessionStatus } from '#client/session.ts'
Expand Down Expand Up @@ -227,6 +228,7 @@ export function LoginRoute(handle: Handle<LoginFormSetup>) {
<input
type="checkbox"
name="rememberMe"
mix={[...checkbox()]}
css={{
marginTop: '0.15rem',
}}
Expand Down
118 changes: 118 additions & 0 deletions docs/agents/remix/beta-5-adoption-audit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Remix v3 Beta 5 adoption audit

Audited against `remix@3.0.0-beta.5` and the upstream
[Beta 5 release](https://github.com/remix-run/remix/releases/tag/remix%403.0.0-beta.5).

## Current state

- `package.json` pins `remix` to `3.0.0-beta.5`; `bun.lock` resolves the same
version.
- Client runtime imports already use `remix/ui` and `remix/ui/jsx-runtime`. The
compatibility runtime in `client/remix-ui-compat/jsx-runtime.ts` preserves the
existing `css` and `on` JSX props while they are migrated incrementally to
native `mix` composition.
- Server routing already uses `remix/fetch-router`, and the data layer uses the
Beta 5-compatible `remix/data-table` API.

## Prioritized recommendations

### High

1. **Minify production browser bundles — implemented.** `package.json` now
passes `--minify` to the production esbuild commands for both
`client/entry.tsx` and `client/mcp-apps/calculator-widget.ts`. The
development watchers remain unminified. This adopts the relevant production
asset improvement from the Beta 5 `remix new` template without replacing this
app's esbuild and Wrangler pipeline.
2. **Adopt `remix/ui/checkbox` for the existing checkbox controls —
implemented.** `client/routes/login.tsx` and `client/routes/admin-agents.tsx`
now apply the first-party checkbox mixin to all three checkboxes. These
controls have native semantics already, so the change adds consistent focus,
disabled, checked, and mixed-state styling without changing form behavior.
3. **Keep active framework guidance on the Beta 5 entrypoints — implemented.**
The active Remix index and TypeScript setup notes now describe `remix/ui`
rather than the removed `remix/component` entrypoint. The one direct
`@remix-run/cookie` application import in `server/auth-session.ts` now uses
the supported umbrella export, `remix/cookie`.

### Medium

1. **Create app-branded button mixins on top of `remix/ui/button`.** Native
buttons repeat similar primary, neutral, ghost, and destructive styles in
`client/routes/chat.tsx`, `client/routes/admin-agents.tsx`,
`client/routes/login.tsx`, `client/routes/oauth-authorize.tsx`,
`client/routes/reset-password.tsx`, `client/app.tsx`,
`client/notifications.tsx`, and `client/editable-text.tsx`. A small app-owned
mixin layer could compose the first-party focus, disabled, and default button
behavior with the tokens in `client/styles/tokens.ts`. Adopting the stock
visual tones directly would replace the pea brand colors, so this needs a
focused visual design pass rather than a mechanical swap.
2. **Adopt `remix/ui/input` in auth and admin forms.**
`client/routes/login.tsx`, `client/routes/reset-password.tsx`, and
`client/routes/admin-agents.tsx` repeat input frame and focus styles. Start
with one complete form and verify focus, autofill, validation, and disabled
states before expanding.
3. **Evaluate `remix/ui/select` for the admin model picker.**
`client/routes/admin-agents.tsx` has the only native select. The first-party
Select would add a consistent popover, keyboard navigation, and typeahead,
but the current disabled visual divider and the untested admin workflow need
explicit coverage before changing behavior.
4. **Centralize canonical-origin resolution.**
`server/handlers/password-reset.ts` prefers `APP_BASE_URL`, while OAuth/MCP
metadata and redirects mostly use `request.url`. If another ingress or
canonical domain is added, use one helper that prefers `APP_BASE_URL` and
otherwise falls back to the native request origin.

### Low or deferred

1. **Do not replace `client/agent-multi-select-combobox.tsx` wholesale.** It is
a multi-select with a minimum-one selection rule, search, keyboard
navigation, and chat-specific labels. Beta 5's high-level Combobox and Select
are single-value controls. A future change could adopt only
`remix/ui/popover` and lower-level listbox primitives while retaining the
app-owned multi-select state, but this is not a drop-in simplification.
2. **Retire the JSX compatibility runtime only as a dedicated migration.**
`client/remix-ui-compat/jsx-runtime.ts` supports hundreds of existing
`css`/`on` props. Converting all call sites to native `mix` in this audit
would create a broad, low-signal diff.
3. **Do not add unused primitives.** Accordion, breadcrumbs, menu, radio, tabs,
and toggle have no matching UI in the current app. The calculator MCP app is
server-rendered HTML plus vanilla browser code, so Remix UI primitives do not
apply to it.

## `trustProxy`

`trustProxy` is not useful in the current deployment topology. It configures
`createRequest()` and `createRequestListener()` from `remix/node-fetch-server`
when a Node HTTP server is reachable only through a trusted reverse proxy. This
app instead enters through `worker/index.ts` as a Cloudflare Worker and passes
the runtime's native `Request` directly through `server/handler.ts` to
`remix/fetch-router`.

The Worker-specific handling should remain:

- `server/audit-log.ts` prefers Cloudflare's `CF-Connecting-IP`.
- `server/auth-session.ts` uses the forwarded protocol only to determine the
cookie's `Secure` attribute.
- `APP_BASE_URL` provides a canonical origin for password-reset links.

Revisit `trustProxy` only if a Node server using `remix/node-fetch-server` is
added and clients cannot bypass a proxy that overwrites forwarded headers.

## Beta 5 template comparison

The default `remix new` template is a Node application using `remix/assets`,
`remix/node-fetch-server`, server/client frame resolution, and a `start` script.
Pea is intentionally different: Wrangler starts the Cloudflare Worker, the
Workers `ASSETS` binding serves static files, and esbuild creates the browser
bundles.

The template improvements map as follows:

| Beta 5 template improvement | Pea action |
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| Production `NODE_ENV=production` | Not needed for the Workers runtime; application code does not branch on `NODE_ENV`. |
| Minified browser assets | Adopted in the production esbuild commands. |
| Client/server `Frame` resolution | Not applicable because pea does not render Remix UI `Frame` components. |
| Dev-server watcher instead of asset-server watcher | Not applicable; `cli.ts` already runs dedicated esbuild watchers with Wrangler. |
| Node production `start` script | Not applicable; `bun run deploy` builds and deploys `worker/index.ts`, and Cloudflare starts Worker isolates. |
27 changes: 11 additions & 16 deletions docs/agents/remix/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ Docs for every package in https://github.com/remix-run/remix/tree/main/packages.

## Start here

- Building UI with Remix Component: [component](./component/index.md)
- Building UI with Remix UI: [Beta 5 adoption audit](./beta-5-adoption-audit.md)
and the installed `@remix-run/ui` README
- Routing and request handling: [fetch-router](./fetch-router/index.md) +
[route-pattern](./route-pattern.md)
- Sessions and cookies: [session](./session/index.md) +
Expand All @@ -39,27 +40,22 @@ Docs for every package in https://github.com/remix-run/remix/tree/main/packages.
## pea adoption snapshot

- Primary runtime packages in active use:
- `remix/component`
- `remix/ui`
- `remix/fetch-router`
- `remix/data-schema`
- `remix/data-table`
- D1 integration uses `remix/data-table` with a repository adapter
(`worker/d1-data-table-adapter.ts`) instead of `remix/data-table-sqlite`.
- Package coverage audit against installed `remix@3.0.0-alpha.3` top-level
exports: no missing Remix package docs in this index.
- The installed framework version and relevant Beta 5 adoption opportunities are
recorded in the [Beta 5 adoption audit](./beta-5-adoption-audit.md).

## UI and components

- [component](./component/index.md)
- [Getting started](./component/getting-started.md)
- [Components](./component/components.md)
- [Styling basics](./component/styling-basics.md)
- [Animate basics](./component/animate-basics.md)
- [Testing](./component/testing.md)
- [interaction](./interaction/index.md)
- [Event listeners and interactions](./interaction/listeners.md)
- [Containers and disposal](./interaction/containers-and-disposal.md)
- [Custom interactions and typed targets](./interaction/custom-interactions.md)
- [Beta 5 adoption audit](./beta-5-adoption-audit.md)
- The `remix/ui` runtime and first-party components are documented in the
installed `node_modules/@remix-run/ui/README.md`.
- `component/` and `interaction/` contain historical alpha documentation and are
not current Beta 5 API guidance.

## Routing and requests

Expand Down Expand Up @@ -144,7 +140,7 @@ Docs for every package in https://github.com/remix-run/remix/tree/main/packages.
| Package | Focus | Docs |
| -------------------------- | ------------------------------------------ | ------------------------------------------------------------- |
| async-context-middleware | AsyncLocalStorage context for fetch-router | [async-context-middleware](./async-context-middleware.md) |
| component | Remix Component UI system | [component](./component/index.md) |
| ui | Runtime and first-party UI components | [Beta 5 audit](./beta-5-adoption-audit.md) |
| compression-middleware | Response compression for fetch-router | [compression-middleware](./compression-middleware/index.md) |
| cookie | Cookie parsing, signing, and serialization | [cookie](./cookie.md) |
| data-schema | Runtime validation and schema parsing | [data-schema](./data-schema.md) |
Expand All @@ -161,7 +157,6 @@ Docs for every package in https://github.com/remix-run/remix/tree/main/packages.
| fs | Lazy file system utilities | [fs](./fs.md) |
| headers | Header parsing and helpers | [headers](./headers/index.md) |
| html-template | Safe HTML template tag | [html-template](./html-template.md) |
| interaction | Event helpers and interactions | [interaction](./interaction/index.md) |
| lazy-file | Streaming File/Blob implementation | [lazy-file](./lazy-file.md) |
| logger-middleware | Request/response logging | [logger-middleware](./logger-middleware.md) |
| method-override-middleware | HTML form method override | [method-override-middleware](./method-override-middleware.md) |
Expand Down
80 changes: 22 additions & 58 deletions docs/agents/remix/remix.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,72 +10,36 @@ See [remix.run](https://remix.run) for framework docs.

## Installation

This repository pins the Beta 5 release:

```sh
npm i remix
bun add remix@3.0.0-beta.5 --exact
```

## Package usage in Remix 3 alpha
## Package usage in Remix 3 Beta 5

The `remix` package is used through subpath imports.

- ✅ `import { createRouter } from 'remix/fetch-router'`
- ✅ `import { route } from 'remix/fetch-router/routes'`
- ✅ `import { createRoot } from 'remix/component'`
- ❌ `import { ... } from 'remix'` (root import removed in `3.0.0-alpha.3`)

## Subpath export surface (`3.0.0-alpha.3`)

Top-level package exports currently include:

- `remix/async-context-middleware`
- `remix/component`
- `remix/compression-middleware`
- `remix/cookie`
- `remix/data-schema`
- `remix/data-table`
- `remix/fetch-proxy`
- `remix/fetch-router`
- `remix/file-storage`
- `remix/file-storage-s3`
- `remix/form-data-middleware`
- `remix/form-data-parser`
- `remix/fs`
- `remix/headers`
- `remix/html-template`
- `remix/interaction`
- `remix/lazy-file`
- `remix/logger-middleware`
- `remix/method-override-middleware`
- `remix/mime`
- `remix/multipart-parser`
- `remix/node-fetch-server`
- `remix/response`
- `remix/route-pattern`
- `remix/session`
- `remix/session-middleware`
- `remix/session-storage-memcache`
- `remix/session-storage-redis`
- `remix/static-middleware`
- `remix/tar-parser`

Plus adapter/data helper subpaths and utility subpaths:

- `remix/data-schema/checks`, `remix/data-schema/coerce`,
`remix/data-schema/lazy`
- `remix/data-table-mysql`, `remix/data-table-postgres`,
`remix/data-table-sqlite`
- `remix/fetch-router/routes`
- `remix/component/jsx-runtime`, `remix/component/jsx-dev-runtime`,
`remix/component/server`
- `remix/interaction/form`, `remix/interaction/keys`,
`remix/interaction/popover`, `remix/interaction/press`
- `remix/response/compress`, `remix/response/file`, `remix/response/html`,
`remix/response/redirect`
- `remix/route-pattern/specificity`
- `remix/session/cookie-storage`, `remix/session/fs-storage`,
`remix/session/memory-storage`
- `remix/file-storage/fs`, `remix/file-storage/memory`
- `remix/multipart-parser/node`
- ✅ `import { createRoot } from 'remix/ui'`
- ✅ `import checkbox from 'remix/ui/checkbox'`
- ❌ `import { ... } from 'remix'` (there is no root API entrypoint)
- ❌ `import { ... } from 'remix/component'` (replaced by `remix/ui`)

The installed `remix/package.json` is the source of truth for the complete
export list. Entry points used by pea include:

- UI: `remix/ui`, `remix/ui/jsx-runtime`, and focused `remix/ui/*` component
modules
- Routing: `remix/fetch-router` and `remix/fetch-router/routes`
- Data: `remix/data-schema`, `remix/data-table`, and `remix/data-table/sqlite`
- Responses: `remix/html-template` and `remix/response/html`
- Cookies: `remix/cookie`

See the [Beta 5 adoption audit](./beta-5-adoption-audit.md) for repository-
specific recommendations, the `trustProxy` assessment, and the default-template
comparison.

## Navigation

Expand Down
15 changes: 7 additions & 8 deletions docs/agents/remix/update.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,18 @@ Keep each Markdown file to roughly 200 lines or fewer. If a README grows beyond
that, split it into multiple files and update the package `index.md` to link the
new chunks.

## 3) Refresh component docs
## 3) Refresh UI docs

`component` is the only package with a `docs` directory. Sync every file from:
Remix Beta 5 replaced the alpha `component` and `interaction` entrypoints with
`ui`. Refresh the current package README from:

```
https://github.com/remix-run/remix/tree/main/packages/component/docs
https://github.com/remix-run/remix/tree/main/packages/ui
```

Update the split files in `docs/agents/remix/component/` to match upstream. Keep
all docs: `animate`, `components`, `composition`, `context`, `events`,
`getting-started`, `handle`, `interactions`, `patterns`, `spring`, `styling`,
`testing`, `tween`. If any single doc exceeds roughly 200 lines, split it into
multiple files and add links in `component/index.md`.
Keep `docs/agents/remix/beta-5-adoption-audit.md` focused on this repository's
usage. The existing `component/` and `interaction/` directories are historical
alpha references; do not present them as current API guidance.

## 4) Keep the index current

Expand Down
3 changes: 2 additions & 1 deletion docs/agents/typescript-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ We have **three** distinct TypeScript environments, each with its own

- **Config**: `types/tsconfig-client.json`
- **Files**: `client/**/*.ts`, `client/**/*.tsx`
- **Environment**: browser (`DOM`, `DOM.Iterable`) + JSX (`remix/component`)
- **Environment**: browser (`DOM`, `DOM.Iterable`) + JSX (`remix/ui` through
`client/remix-ui-compat`)

### Tools

Expand Down
2 changes: 0 additions & 2 deletions mcp/mcp-server-e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -625,9 +625,7 @@ test(
calculatorWidgetResponse.headers.get('access-control-allow-origin'),
).toBe('*')
const calculatorWidgetSource = await calculatorWidgetResponse.text()
expect(calculatorWidgetSource).toContain('createWidgetHostBridge')
expect(calculatorWidgetSource).toContain('Calculator result:')
expect(calculatorWidgetSource).toContain('sendUserMessageWithFallback')
expect(calculatorWidgetSource).toContain('ui/initialize')
expect(calculatorWidgetSource).toContain('ui/message')

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
"dev:mock-resend": "bun ./wrangler-env.ts dev --local --config mock-servers/resend/wrangler.jsonc",
"dev:mock-ai": "bun ./wrangler-env.ts dev --local --config mock-servers/ai/wrangler.jsonc",
"deploy": "bun run build && bun ./wrangler-env.ts deploy",
"build:mcp-apps": "esbuild client/mcp-apps/calculator-widget.ts --bundle --format=esm --target=es2022 --outdir=public/mcp-apps --platform=browser",
"build:client:web": "esbuild client/entry.tsx --bundle --format=esm --target=es2022 --outdir=public --entry-names=client-entry --chunk-names=assets/[name] --asset-names=assets/[name] --jsx=automatic --jsx-import-source=#client/remix-ui-compat",
"build:mcp-apps": "esbuild client/mcp-apps/calculator-widget.ts --bundle --format=esm --target=es2022 --outdir=public/mcp-apps --platform=browser --minify",
"build:client:web": "esbuild client/entry.tsx --bundle --format=esm --target=es2022 --outdir=public --entry-names=client-entry --chunk-names=assets/[name] --asset-names=assets/[name] --jsx=automatic --jsx-import-source=#client/remix-ui-compat --minify",
"build:client": "bun run build:client:web && bun run build:mcp-apps",
"build": "bun run build:client && bun ./wrangler-env.ts build",
"lint": "oxlint .",
Expand Down
2 changes: 1 addition & 1 deletion server/auth-session.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createCookie } from '@remix-run/cookie'
import { createCookie } from 'remix/cookie'

const defaultSessionMaxAgeSeconds = 60 * 60 * 24 * 7
const rememberedSessionMaxAgeSeconds = 60 * 60 * 24 * 30
Expand Down
Loading