Packages

@remix-run/ui

0.13.0 · 23 Oct 2026MIT19.4k / weekRepositoryHomepagenpmAtom feed

Your version

17 stable releases, newest first, by release line. Notes are quoted as the maintainers published them.

  1. 0.13.x · latest line
  2. 0.13.0

    23 Oct 2026
    • breaking

      Prop types for the previously removed styled Accordion, Select, and Combobox components have also been removed: AccordionProps, AccordionItemProps, AccordionTriggerProps, AccordionContentProps, SelectProps, SelectOptionProps, ComboboxProps, and ComboboxOptionProps. Accordion's unused headingLevel prop and corresponding context fields, along with the AccordionHeadingLevel type, have also been removed.

    • breaking

      The toggle mixin is now exported as toggle instead of control. Since toggle consists of a single mixin, it can now be called directly as toggle() rather than grouped under toggle.control().

      -import * as toggle from '@remix-run/ui/toggle'
      +import { toggle } from '@remix-run/ui/toggle'
      
      -<input mix={toggle.control({ defaultChecked: true })} />
      +<input mix={toggle({ defaultChecked: true })} />

      The options type has also been renamed from ToggleControlOptions to ToggleOptions:

      -import type { ToggleControlOptions } from '@remix-run/ui/toggle'
      +import type { ToggleOptions } from '@remix-run/ui/toggle'
    • Check the user's current motion preference with prefersReducedMotion() from @remix-run/ui/animation. It returns false during server rendering or when matchMedia is unavailable, so it can be called safely from component render functions as well as browser event handlers.

      import { animateLayout, prefersReducedMotion, spring } from '@remix-run/ui/animation'
      
      function Card() {
        return () => (
          <div mix={animateLayout(prefersReducedMotion() ? false : { ...spring('snappy') })}>Details</div>
        )
      }
  3. 0.12.x
  4. 0.12.1

    1 Oct 2026
    • Bumped @remix-run/* dependencies:

  5. 0.12.0

    1 Oct 2026
    • breaking

      The core component APIs provided by @remix-run/ui have now moved to @remix-run/component. @remix-run/ui now provides headless, accessible UI primitives and animation utilities for Remix components.

      The package root and its JSX runtime, server-rendering, test, development-refresh, and styled-component exports are no longer available. Move runtime imports to @remix-run/component or remix/component:

      -import { createRoot, css, on } from '@remix-run/ui'
      +import { createRoot, css, on } from '@remix-run/component'

      If jsxImportSource currently points to @remix-run/ui or remix/ui, move it to the corresponding component entrypoint:

       {
         "compilerOptions": {
           "jsx": "react-jsx",
      -    "jsxImportSource": "@remix-run/ui"
      +    "jsxImportSource": "@remix-run/component"
         }
       }

      Primitive APIs retain their existing contracts but now use flat package subpaths:

      -import * as accordion from '@remix-run/ui/accordion/primitives'
      +import * as accordion from '@remix-run/ui/accordion'

      The package exports the accordion, anchor, animation, combobox, listbox, menu, popover, select, tabs, and toggle subpaths. The previous styled breadcrumbs, button, checkbox, input, and radio modules have been removed.

      @remix-run/ui is currently unstable and versioned independently. It is not available through the remix package and must be installed and imported directly. For an application that otherwise uses the remix package:

      npm i remix @remix-run/ui

      If the asset server restricts imports with allowPackages, add @remix-run/ui now that these APIs are imported from the standalone package:

       {
         "assets": {
      -    "allowPackages": ["remix"]
      +    "allowPackages": ["@remix-run/ui", "remix"]
         }
       }

      Direct-package consumers should install both @remix-run/component and @remix-run/ui when they use the component runtime with the primitives or animation utilities.

    • Bumped @remix-run/* dependencies:

  6. 0.11.x
  7. 0.11.0

    28 Sep 2026
    • Preserve client-owned attributes during frame reloads with data-rmx-preserve-attrs="class data-theme" on any matched element, including <html> and <body> (see #11809). The named attributes keep their live values or absence while other attributes and children reconcile normally. The incoming HTML controls the list; clearing it or removing a name returns those attributes to normal reconciliation.

    • Send X-Remix-Frame: true from the default browser resolver for all frame loads, including top-frame navigation and reloads. Named frames also send X-Remix-Target, allowing handlers to identify and target frame requests consistently across browser and server rendering (see #11941).

    • Treat marker-less HTML that starts with a doctype or <html> as a document reload, so pages rendered with renderToString can be navigated by the client runtime (see #11808).

  8. 0.10.x
  9. 0.10.0

    18 Sep 2026
    • breaking

      Navigations that specify a named frame that is not currently mounted now perform a document navigation instead of reloading the top frame. Fresh links, forms, and navigate() calls are left to the browser, preserving native form methods and bodies, while back and forward traversal reloads the destination document. Omit the target when the navigation should always reload the top frame:

      -<a href="/account" data-rmx-target="optional-account">
      +<a href="/account">
    • breaking

      The default resolveFrame now only fetches same-origin sources and follows same-origin redirects. Apps that load cross-origin frame content must provide a custom resolveFrame to run().

      Validate navigation source overrides regardless of the target, falling back to document navigation for invalid or cross-origin overrides.

    • breaking

      Raw HTML props now require an opaque value created by unsafeHTML(). This applies to innerHTML and both iframe srcDoc spellings (srcDoc and srcdoc). It prevents attacker-controlled prop spreads from activating HTML parsing with plain strings or JSON-shaped objects. outerHTML is not supported because it would replace a reconciler-owned element. unsafeHTML() is an explicit authorization boundary; it does not sanitize or otherwise modify its input.

      -import type { Handle } from 'remix/ui'
      +import { unsafeHTML } from 'remix/ui'
      +import type { Handle } from 'remix/ui'
      
       function Content(handle: Handle<{ html: string }>) {
      -  return () => <div innerHTML={handle.props.html} />
      +  return () => <div innerHTML={unsafeHTML(handle.props.html)} />
       }
  10. 0.9.x
  11. 0.9.0

    8 Sep 2026
    • Client entries can now provide import maps through resolveClientEntry(). The <ImportMap> component from remix/ui/server combines your mappings with those from blocking client entries so the initial document contains one complete import map. New mappings from later frame responses are installed before their client entries load. Plain <script type="importmap"> elements remain supported when you do not need to combine mappings (see #11706).

      When a frame response changes an installed import mapping or integrity value, Remix loads a fresh document so navigation after a deployment cannot mix old and new modules.

    • run() now accepts a processClientEntryPreloads callback to handle preloads for client entries discovered after the initial page load. Use it with remix/multiple-import-maps-polyfill to preload modules in browsers that need the polyfill, returning an empty array to skip native modulepreload links. See the client entry setup example (see #11706).

    • handle.update() now warns and skips the extra render when called during component setup. Calls during rendering, or before the initial commit from outside setup, report a clear component error. Move these updates into an event handler or a handle.queueTask() callback (see #11795).

  12. 0.8.x
  13. 0.8.0

    31 Aug 2026
    • breaking

      Remix UI framework-owned DOM attributes now consistently use the data-rmx-* namespace. Rename rmx-document, rmx-target, rmx-src, rmx-history, rmx-reset-scroll, rmx-preserve-dom, and data-key to data-rmx-document, data-rmx-target, data-rmx-src, data-rmx-history, data-rmx-reset-scroll, data-rmx-preserve-dom, and data-rmx-key. Generated style and module preload markers now use data-rmx-style and data-rmx-module-preload instead of data-rmx.

    • breaking

      During server rendering, script elements with non-string children previously serialized those children as escaped HTML text. They now render empty and report an error. Pass a single string child, such as JSON.stringify(value), to embed script content without HTML entity escaping. Script-tag sequences that could terminate the element remain escaped.

    • breaking

      Remove addEventListeners(). Use native target.addEventListener(type, listener, { signal }) instead. If a listener used the helper's second callback argument, create an AbortController and abort it when the listener runs again or its lifetime signal aborts.

  14. 0.7.x
  15. 0.7.0

    17 Aug 2026
    • run() now uses a default browser frame resolver when resolveFrame is omitted. Apps that supplied a resolver only to fetch frame HTML can remove it:

       let app = run({
         loadModule,
      -  resolveFrame(src, options) {
      -    return fetch(src, {
      -      headers: { Accept: 'text/html' },
      -      signal: options?.signal,
      -    })
      -  },
       })

      All run() calls now enable frame reloads and same-origin link and form navigation through the Navigation API. The default resolver submits the requested method, encoding, and form data, and rejects non-OK responses. Keep a custom resolveFrame when the app needs custom request headers, body encoding, response handling, or error UI. Add rmx-document to a link or form to leave that navigation to the browser (see #11693).

  16. 0.6.x
  17. 0.6.0

    17 Aug 2026
    • Allow resolveClientEntry() to return module preload hrefs so client entries can preload their browser module graphs

  18. 0.5.x
  19. 0.5.0

    14 Aug 2026
    • breaking

      Browser resolveFrame callbacks now receive a single options object instead of positional signal and target arguments. Update resolveFrame(src, signal, target) implementations to use resolveFrame(src, options) and read options?.signal and options?.target.

    • Added frames to the app object returned from run(), mirroring the existing handle.frames API

    • Added @remix-run/ui/dev/refresh for development tooling that needs to reconcile mounted roots after component modules update.

  20. 0.4.x
  21. 0.4.0

    1 Jul 2026

    Reading the note…

  22. 0.3.x
  23. 0.3.0

    29 May 2026

    Reading the note…

  24. 0.2.x
  25. 0.2.0

    20 May 2026

    Reading the note…

  26. 0.1.x
  27. 0.1.2

    20 May 2026

    Reading the note…

  28. 0.1.1

    30 Apr 2026

    Reading the note…

  29. 0.1.0

    29 Apr 2026

    Reading the note…

  30. 0.0.x
  31. 0.0.0

    29 Apr 2026

    Reading the note…