惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Vercel News
Vercel News
O
OpenAI News
Engineering at Meta
Engineering at Meta
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
WordPress大学
WordPress大学
宝玉的分享
宝玉的分享
GbyAI
GbyAI
T
The Blog of Author Tim Ferriss
Google DeepMind News
Google DeepMind News
B
Blog RSS Feed
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
云风的 BLOG
云风的 BLOG
罗磊的独立博客
S
SegmentFault 最新的问题
The Register - Security
The Register - Security
Hugging Face - Blog
Hugging Face - Blog
D
DataBreaches.Net
U
Unit 42
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
B
Blog
阮一峰的网络日志
阮一峰的网络日志
P
Proofpoint News Feed
雷峰网
雷峰网
V
Visual Studio Blog
小众软件
小众软件
aimingoo的专栏
aimingoo的专栏
N
Netflix TechBlog - Medium
酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Y
Y Combinator Blog
博客园 - 【当耐特】
G
Google Developers Blog
L
LangChain Blog
Stack Overflow Blog
Stack Overflow Blog
I
InfoQ
Martin Fowler
Martin Fowler
F
Fortinet All Blogs
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
The Cloudflare Blog
AI
AI
Google Online Security Blog
Google Online Security Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
博客园 - Franky
Blog — PlanetScale
Blog — PlanetScale
Webroot Blog
Webroot Blog
PCI Perspectives
PCI Perspectives
爱范儿
爱范儿
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org

Kent C. Dodds Blog

Implementing Hybrid Semantic + Lexical Search Simplifying Containers with Cloudflare Sandboxes Migrating to Workspaces and Nx Offloading FFmpeg with Cloudflare Building Semantic Search on my Content Helping YOU ask ME questions with AI How I used Cursor to Migrate Frameworks The Dow's Start on the Covenant Path 2025 in Review The next chapter: EpicAI.pro AI is taking your job How I increased my visibility Launching Epic Web 2023 in Review Stop Being a Junior RSC with Dan Abramov and Joe Savona Live Stream Fixing a Memory Leak in a Production Node.js App 2022 in Review My Car Accident I Migrated from a Postgres Cluster to Distributed SQLite with LiteFS I'm building EpicWeb.dev A review of my time at Remix Remix: The Yang to React's Yin How I help you build better websites Why I Love Remix The State Initializer Pattern How to React ⚛️ Get a catch block error message with TypeScript Building an awesome image loading experience How Remix makes CSS clashes predictable Introducing the new kentcdodds.com How I built a modern website in 2021 How to use React Context effectively Static vs Unit vs Integration vs E2E Testing for Frontend Apps The Testing Trophy and Testing Classifications Array reduce vs chaining vs for loop Don't Solve Problems, Eliminate Them Super Simple Start to Remix Super Simple Start to ESModules in Node.js JavaScript Pass By Value Function Parameters How to write a Constrained Identity Function (CIF) in TypeScript How to optimize your context value How to write a React Component in TypeScript TypeScript Function Syntaxes Listify a JavaScript Array Build vs Buy: Component Libraries edition Using fetch with TypeScript Wrapping React.useState with TypeScript Define function overload types with TypeScript 2020 in Review Business and Engineering alignment Hi, thanks for reaching out to me 👋 useEffect vs useLayoutEffect Super simple start to Firebase functions Super simple start to Netlify functions Super Simple Start to css variables Favor Progress Over Pride in Open Source Testing Implementation Details How getting into Open Source has been awesome for me useState lazy initialization and function updates Use ternaries rather than && in JSX Application State Management with React Use react-error-boundary to handle errors in React JavaScript to Know for React How I structure Express apps What open source project should I contribute to? When I follow TDD AHA Programming 💡 How I Record Educational Videos Should I write a test or fix a bug? Stop mocking fetch Intentional Career Building Improve test error messages of your abstractions Tracing user interactions with React Eliminate an entire category of bugs with a few simple tools Common mistakes with React Testing Library Super Simple Start to React The State Reducer Pattern with React Hooks Function forms Replace axios with a simple custom fetch wrapper How to test custom React hooks React Production Performance Monitoring Should I useState or useReducer? Stop using isLoading booleans Make Your Test Fail Make your own DevTools An Argument for Automation Fix the "not wrapped in act(...)" warning Super Simple Start to ESModules in the Browser Implementing a simple state machine library in JavaScript 2010s Decade in Review Why users care about how you write code Why I avoid nesting closures Don't call a React function component Why your team needs TestingJavaScript.com Inversion of Control Understanding React's key prop How to Enable React Concurrent Mode How to add testing to an existing project Profile a React App for Performance
Stop using client-side route redirects
2020-04-13 · via Kent C. Dodds Blog

First, let's make sure we're talking about the same thing:

function App() {
	return (
		<BrowserRouter>
			<Redirect from="/old-route" to="/new-route" />
			{/* ... more routes etc... */}
		</BrowserRouter>
	)
}

(Or however else you might accomplish the same thing in your own framework).

I'm NOT talking about this kind of usage (example borrowed from the docs):

<Route exact path="/">
	{loggedIn ? <Redirect to="/dashboard" /> : <PublicHomePage />}
</Route>

That's technically fine, though there may be a better way to handle this in your app. Read my post Authentication in React Applications for more about this.

Ok, so now that we're clear on that, let me explain why and then what you should be doing instead.

First, the goal of using a client-side redirect (like with Redirect's from prop or similar) is to ensure the user doesn't land on a URL that you expect they could land on and instead redirects them to a different one. A great example of this is when you change the URL scheme of your app. Or perhaps you sent an email out to your users with the wrong URL and now you're stuck redirecting users for all eternity.

Whatever the case may be... If you know the URL you want to redirect users from, then you're better off redirecting them on the server. Let's talk about why client-side redirects are such a problem.

The 149.3 kilobyte redirect 😱

Let's say a user comes to your app and hits the /old-route. Before the browser knows it's on a bad route, they have to download your app. Regardless of how good you are at code splitting, they're going to need at least react, react-dom, and react-router-dom. That's some bytes!

  • react@16.13.1 - 6.3kB
  • react-dom@16.13.1 - 114.6kB
  • react-router-dom@5.1.2 - 28.4kB

These sizes are minified but uncompressed, because compression depends on too many variables for accurate comparisons. Also, I realize that React Router v6 is coming soon and will reduce the bundle size significantly, but hear me out!

And that doesn't even include your own code!

Now, let's not be disingenuous! <Redirect /> only supports redirecting within the same app, so the user's going to need to download all of that stuff anyway. But the concept still applies: Don't do a client-side redirect from a known route to another known route. If it's from one app to another, then the user just downloaded and ran a bunch of code they didn't need. Also, even if it's within-app, then depending on how you have things configured with code-splitting, it's definitely possible that this resulted in the user downloading too much or kicking off a waterfall effect (resulting in suboptimal performance).

As for using <Redirect /> when it is within your own app, the user will have to wait until the app finishes loading and is actually run before the URL in their address bar is updated to the correct URL. Maybe not reason enough by itself to avoid <Redirect /> with the from prop, but keep reading.

HTTP Status Codes

This is definitely reason enough on its own to avoid client-side redirects (like <Redirect /> with the from prop). When a user is redirect via JavaScript, there's no way to inform the browser (or search engines) that the URL they were on before is no longer correct and the new one is correct. This has implications on search engines and the browser cache (also interesting cache handling for error status codes).

Server side redirects

I'll show you three server-side redirects that I implemented for bookshelf.lol. In the bookshelf app, I don't have anything at the home route of / (note that the bookshelf app is purely for educational purposes, so semi-contrived decisions like this are not to be questioned. Please and thank you).

So instead, every request to / should be redirected to /list. And I want to redirect with a 302 because it's quite possible that at some point in the future I'll change my mind and I don't want to have to deal with busting the browser or search engine cache of this particular redirect.

Netlify (production)

With Netlify, you can configure redirects in a netlify.toml file, but I chose the _redirects file option. Here's what that file needs to get that redirect in place:

/  /list  302!

You can check out this file as it stands today right here.

If I wanted to use the netlify.toml file, then it would be more like this:

[[redirects]]
  from = "/"
  to = "/list"
  status = 302
  force = true

Locally built file

Once I've built the bookshelf app, if I want to run it locally, I use serve to do so. This CLI uses serve-handler which can be configured with serve.json like so:

{
	"redirects": [
		{
			"source": "/",
			"destination": "/list",
			"type": 302
		}
	]
}

You can check out this file as it stands today right here.

Locally during development

The bookshelf app is built with Create React App (and it uses react-scripts). This means that the dev server is completely handled for me behind the scenes. Luckily, react-scripts does allow for customization of the server for proxy purposes and I can use this for the redirect by creating a file at src/setupProxy.js. This simply exports a function which accepts the express app and then I can use res.redirect when the / route is requested:

function proxy(app) {
	app.get(/^\/$/, (req, res) => res.redirect('/list'))
}

module.exports = proxy

You can check out this file as it stands today right here.

Conclusion

Whatever host you end up publishing your app to probably has some way to configure redirects (if it doesn't that's super weird of them). It may feel like a lot of work to avoid a simple:

<Redirect from="/" to="/list" />

But really the cost is pretty small (set it up once and forget about it) and the benefits of being semantically correct about this outweighs those costs.

In general, following semantics of the web is better for existing tools (just like how using semantic markup is better for accessibility). If the web standards satisfy the use case, then use the web standard.

Thank you to Ryan Florence (creator of React Router) who convinced me of this idea.