Skip to content

Releases: remix-run/remix

v1.0.0-rc.3

22 Nov 18:28
Compare
Choose a tag to compare
v1.0.0-rc.3 Pre-release
Pre-release

Bug fixes 🐛

  • Fixed a bug in <Form> where requests were sent to the wrong action (#411).
  • Fixed a bug with await handlers in server runtime (#413).
  • Fixed a few bugs with local development (#417, #431)

Breaking changes 💔

  • react-router-dom is no longer a peer dependency, so you can remove it from your package.json (#424). This isn't technically a breaking change today, but it could break your app in the future without making this switch! All React Router modules should be imported directly from remix:
// before
import { Outlet } from "react-router-dom";
// after
import { Outlet } from "remix";

Full Changelog: v1.0.0-rc.2...v1.0.0-rc.3

v1.0.0-rc.2

20 Nov 00:07
Compare
Choose a tag to compare
v1.0.0-rc.2 Pre-release
Pre-release

The best way to find a bug in software is to ship it. On a related note, we shipped rc.1 and found a bug 😅

This is a quick patch release, so if you're upgrading from a v0 release, check out the relevant notes here: https://github.com/remix-run/remix/releases/tag/v1.0.0-rc.1

Full Changelog: v1.0.0-rc.1...v1.0.0-rc.2

v1.0.0-rc.1

19 Nov 21:54
Compare
Choose a tag to compare
v1.0.0-rc.1 Pre-release
Pre-release

Alright friends, we're almost there! Welcome to the first release candidate for Remix v1.0. Buckle up and get ready, let's dive in to what's new!

Features ✨

  • Added <ScrollRestoration /> component that emulates the browser's scroll restoration behavior on client-side navigations (#399). This component is included in the starter template by default, but existing apps can render it in your root.(js|tsx) directly before <Scripts />.

Bug fixes 🐛

  • <Form> will now include values held by named submit buttons (or <input type="submit" />) (#398)

Breaking changes 💔

  • Removed all deprecated hooks (#395)
  • Rename MetaDescriptor type to HtmlMetaDescriptor (#378)

Other odds and ends 🤷‍♂️

  • Created fancy new starter templates for create-remix 💅
  • Updated esbuild to the latest and greatest (#402)
  • Made the CLI shutdown process a bit more graceful (#400)

Full Changelog: v0.21.0...v1.0.0-rc.1

v0.21.0

16 Nov 23:46
Compare
Choose a tag to compare

💅 Features

  • We're on React Router v6! Go change your package.json dependency to "react-router-dom": "^v6.0.0". If you've got "history" in there, you can delete it (it's now just a direct dependency of React Router, your app doesn't need to know about it).
  • You can now get form data directly from the request object! (note that this doesn't yet support multipart/form-data requests, but that is in development right now).
    export function action({ request }) {
      // old
      let body = new URLSearchParams(await request.text());
    
      // new
      let body = await request.formData();
    }
  • Add support for Resource Routes! You can now omit the default export from any route file and the response will be any resource returned from its loader. You can use this to build:
    • RSS feeds
    • dynamically generated images
    • mobile app API routes (so you can reuse the backend logic for both UIs)
    • and lots more!

✨ Improvements

  • You can now add attributes to the <script> tags rendered by <Scripts /> by passing them as props. This allows you to add a Content Security Policy and drop a nonce on the inline scripts that Remix renders so the browser doesn't block them (we'll have a guide soon, but you can read more about CSP on MDN.)
  • Added sourcemaps for server builds.

🐛 Bugfixes

  • Fixed a bug where ?index was included in action requests for index routes.

🗒️ Docs

Full Changelog: v0.20.1...v0.21.0

v0.20.1

29 Oct 22:28
Compare
Choose a tag to compare

Fixed a tiny bug in @remix/create-remix to prevent CLI errors. All good here! 😎

v0.20.0

29 Oct 22:00
Compare
Choose a tag to compare

Gather 'round folks, this release brings along lots of tasty features and stomps out a few gnarly bugs. Here's a high-level overview of important changes:

  • CLI changes
    • remix dev is now remix watch
    • remix run is now remix dev
    • Added remix routes
    • Added remix build --sourcemap
  • <Form forceRefresh /> is now <Form reloadDocument />
  • Added a new handleDataRequest API for manipulating request data
  • Now using [email protected]

Now, let's get into to the weeds a bit 🤓

🕞 tl;dr Upgrading

  • Update your package.json or pm2 configs
    • remix dev -> remix watch
    • remix run -> remix dev
  • <Form forceRefresh /> -> <Form reloadDocument />
  • Ensure you've got [email protected] in your package.json dependencies.

💔 Breaking Changes

  • Several users felt that the remix dev command did not behave as expected. The CLI command remix dev has been changed to remix watch, and remix run has been changed to remix dev to hopefully make the CLI a bit more intuitive. Please note that remix dev requires that @remix-run/serve is installed as a dependency, just as remix run did before (#315).
  • The forceRefresh prop on <Form> has been renamed to reloadDocument. We feel like this more clearly communicates the behavior of the form's submission.

✨ Improvements

  • Now shipping with the latest beta of react-router-dom! 🚀

  • We updated all docs and templates to suggest npx create-remix instead of npm init remix, which is generally more consistent and less error prone for creating new sites with reasonable defaults.

  • TypeScript should now correctly infer types from imported .json modules (#314)

  • Added support for multiple meta tags that use the same key to a route's meta function (#322). We now allow you to pass an array of values to a key in the object returned by meta:

    export let meta: MetaFunction = () => {
      return {
        "article:author": ["Chance the Dev", "Ryan Florence"],
      };
    };
  • We added a new CLI command remix routes that allows you to visualize your app's route structure from the command line in either JSX or JSON format (#326).

  • We introduced handleDataRequest for modifying the response of data requests in routes (#329):

    // example route file
    import type { HandleDataRequestFunction } from "remix";
    
    // this is an optional export!
    export let handleDataRequest: HandleDataRequestFunction = (
      response,
      { request, params, context }
    ) => {
      // do whatever you'd like before returning the response!
      response.headers.set("x-custom", "yay!");
      return response;
    };
  • remix build no longer puts source maps in your browser build by default. This was a security issue because people could see your server side code from the browser! If you want to have source maps in your production builds, you can use the --sourcemap flag 🗺️ (#350).

  • We made a handful of improvements to the new Remix application templates.

🐛 Bugfixes

  • The ~/ path alias for the app directory now works when importing markdown files (#317).
  • We squashed a bug in which form submissions were trimming values with duplicate keys (checkboxes, radio inputs, etc.) (#344).
  • We annihalated a bug in which redirects are not followed when thrown from an action for client-side form submissions (#349).
  • We exterminated a particularly nasty bug in the @remix/architect server that was sending requests to invalid URLs (#333).
  • We wiped out several bugs for sites shipping to Cloudflare Workers (#318).
  • We crushed a bug causing back/forward navigation mayhem (#351)
  • We demolished a bug where <Link prefetch> was prefetching way too much stuff.
  • We fumigated a bug where imports from React Router weren't working in Remix (like <Outlet>).
  • I'm running out of creative synonyms for "fix", so thankfully I'm also out of bug fixes to describe 😅

🗒️ Docs

  • Lots of docs updates and improvements, perhaps too many to capture succinctly in release notes! Be sure to check out https://docs.remix.run for the latest and greatest.

v0.19.3

14 Oct 14:19
Compare
Choose a tag to compare

Couple bug fixes:

  • Build no longer fails on layout routes with actions
  • Can use npm 7+

v0.19.2

10 Oct 04:08
Compare
Choose a tag to compare

Changed layout route convention from _layout to two underscores: __layout. This is a breaking change but it's only been around for 24 hours.

v0.19.1

09 Oct 15:05
Compare
Choose a tag to compare
  • Type fixes
    • EntryContext
    • Route
    • LinksFunction
  • Bugfixes
    • Routes without loaders were being called on client side transitions

v0.19.0

08 Oct 18:40
Compare
Choose a tag to compare

published: 2021-10-07

v0.19.0 Release Notes

Holy smokes this is a big release with tons of good stuff. Some let you handle new use-cases, some clean up your code, and others automatically make your website better and you don't have to do anything. This release puts us within inches of a stable v1.

The biggest piece of work in this release is the rewrite of client side transitions. This enabled us to add a handful of new features, fix some bugs, and make it more efficient for the browser and faster for user at the same time.

When the URL changes Remix does a bunch of communication with the server. We used to have a 300 line useEffect that just kind of did everything. We lovingly referred to it as "the big effect". We knew it was incomplete, but we were waiting to see how the rest of Remix shook out before really tackling this work. The time came and we spent months getting it right. Most of the features in this release are from that work or built on top of it.

tl;dr Upgrade Guide and Breaking Changes

  • Upgrade to [email protected]
  • useRouteData -> useLoaderData
  • usePendingFormSubmit -> useTransition().submission
  • usePendingLocation -> useTransition().location
  • block({ rel: "preload", as: "image", href }) -> Remove the block call, can render a <link rel="prefetch"> wherever you link to the page
  • links({ data }) -> Use <Link prefetch="intent"> for { page } links you used with data and then inline <link /> inside your component based on the useLoaderData instead. Most uses of <link> are "body ok", so you can just render them inside the component instead.
  • Returning a string from actions for a redirect need to actually return redirect(string)

React Router v6.0.0-beta.6

Remix is now compatible with React Router v6.0.0-beta.6. We're days away from launching the stable v6 release over there! You must upgrade your react router dependency for Remix to continue to work properly.

Changes to actions

Actions don't require you to redirect out of them anymore! You can return responses just like loaders now. The data you return is available from useActionData(). This is especially nice for server side form validation errors: just return the errors as an object, no more session/action/loader dance!

import { useActionData, json } from "remix";

export function action({ request }) {
  let body = new URLSearchParams(await request.text());
  let name = body.get("visitorsName");
  return json({ message: `Hello, ${name}` });
}

export default function Invoices() {
  let data = useActionData();
  return (
    <Form method="post">
      <p>
        <label>
          What is your name?
          <input type="text" name="visitorsName" />
        </label>
      </p>
      <p>{data ? data.message : "Waiting..."}</p>;
    </Form>
  );
}

Note about resubmissions: Remix previously required redirects from actions to prevent accidental resubmissions (like booking a flight twice if the user clicks back). If you're rendering <Scripts/> the form will not be resubmitted on back or refresh so you're still protected automatically. However, now that you aren't required to redirect out of actions, Remix can't protect your users from resubmissions when you aren't rendering <Scripts/>. If you are handling forms without JavaScript, we highly recommend you still redirect out of your actions or ensure your actions can be run mutliple times without negative consequences.

Finally, since actions can return data, returning a string will no longer automatically redirect, it will send down the string as data. You'll need to wrap it in redirect(string) when upgrading.

Read more about useActionData

useLoaderData replaces useRouteData()

Because "route data" can come from both loaders and actions now, useRouteData didn't make a lot of sense so we've got two hooks now:

useLoaderData(); // data from your loader
useActionData(); // data from your action

useTransition replaces usePendingLocation and usePendingFormSubmit

With the transition rewrite, we've got a better hook that ecompasses all "pending" information. This hook tells you everything you need to know to build even better loading experiences. For example, you can indicate all phases of the pending form submission to the user. Previous we only knew it was pending and nothing more, now you know everything.

function SubmitButton() {
  let transition = useTransition();
  let text =
    transition.type === "actionSubmission"
      ? "Creating Record"
      : transition.type === "actionRedirect"
      ? "Redirecting to new record..."
      : "Create";
  return <button type="submit">{text}</button>;
}

Updating from the old hooks is pretty straightforward:

// old
usePendingFormSubmit();
// new
useTransition().submission;

// old
usePendingLocation();
// new
useTransition().location;

This hook also sets a solid foundation for us to finish our in-progress automatic scroll restoration, which should come very soon after this release.

There are numerous improvements to client side transitions that don't affect your code, but make your app better. In the case of interrupted navigations and form submissions, Remix previously simply ignored the responses of stale navigation fetches. Now it automatically aborts them using AbortController, saving your user's network bandwidth and the browser doesn't waste CPU cycles processing the response.

Read more about useTransition

Same URL data reloading and hash changes

Without JavaScript, if users click a link to the page they are already on, the browser will request a brand new document but replace the current entry in the history stack. Remix now emulates that behavior by refetching all loaders on the page and replacing the current entry in the history stack.

We also fixed a bug where loaders were called when only the url hash was changing. URL hashes don't go to the server so they no longer cause loaders to be called either, but they are a new location.

useFetcher

While Remix's loaders and actions are great for traditional navigations, modern apps often require more dynamic ways to communicate with the server. This hook enables you to call your loaders and actions outside of a navigation. You might think of it as using your loaders and actions as "API routes". Here are a few examples:

  • Writing a loader that returns data for a <Combobox> auto suggest component
  • A newsletter sign up form at the bottom of multiple pages in your app
  • Any UI where you need to allow multiple actions to be pending at the same time (like a list of records with single click buttons to change their state on the server)
  • Components that fetch data based on user interactions rather than navigation, like a user avatar that pops up their profile when hovered or focused.

Here's an example of marking an article as read:

function useMarkAsRead({ articleId, userId }) {
  let markAsRead = useFetcher();

  useSpentSomeTimeHereAndScrolledToTheBottom(() => {
    markAsRead.submit(
      { userId },
      {
        method: "POST",
        action: `/article/${articleID}/mark-as-read`,
      }
    );
  });
}

After the action completes, Remix will do its normal thing of reloading all loaders on the page after actions to ensure the data shown to the user is the latest data from the server. If multiple actions are pending at the same time, Remix makes sure to commit every fresh respnose and aborts any stale ones. That's right, Remix automatically takes care of race conditions!

Additionally, if you return a redirect from a loader/action being called by a fetcher, Remix will redirect the application to that page. And if any errors are thrown, the nearest error boundary will be rendered as usual. With useFetcher you get all of the same protections as a normal navigation when communicating with the server.

There are a lot more examples in the docs you should go check out:

Read more about useFetcher

unstable_shouldReload

During client side transitions, Remix will optimize reloading of routes that are already rendering, like not reloading layout routes that aren't changing. In other cases, like form submissions or search param changes, Remix doesn't know which routes need to be reloaded so it reloads them all to be safe. This ensures data mutations from the submission or changes in the search params are reflected across the entire page.

This function lets apps further optimize by returning false when Remix is about to reload a route. The most common case is telling Remix to never reload the root route:

export let loader = () => {
  return {
    ENV: {
      CLOUDINARY_ACCT: process.env.CLOUDINARY_ACCT,
      STRIPE_PUBLIC_KEY: process.env.STRIPE_PUBLIC_KEY,
    },
  };
};

export let unstable_shouldReload = () => false;

As always, Remix puts you in charge of the network tab.

Read more about shouldReload

Link Prefetching

This feature is awesome. One of our goals with Remix is to "destroy all spinners". Of course, we have a really great API to help you build great loading UI (useTransition), but the end goal is to not need the spinner in the first place. You can do that by link prefetching:

import { Link, NavLink } from "remix" // not react router!

// prefetch resources when the user seems like they're going to click it
<Link prefetch="intent" />

// prefetch it when this link renders
<NavLink prefetch="render" />

We recommend covering your app with `<Link pref...

Read more