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

推荐订阅源

Google DeepMind News
Google DeepMind News
C
CERT Recently Published Vulnerability Notes
C
Cisco Blogs
Cloudbric
Cloudbric
The Last Watchdog
The Last Watchdog
L
LINUX DO - 热门话题
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Security Archives - TechRepublic
Security Archives - TechRepublic
TaoSecurity Blog
TaoSecurity Blog
V2EX - 技术
V2EX - 技术
H
Heimdal Security Blog
S
Security Affairs
L
Lohrmann on Cybersecurity
Hacker News - Newest:
Hacker News - Newest: "LLM"
Simon Willison's Weblog
Simon Willison's Weblog
WordPress大学
WordPress大学
小众软件
小众软件
Security Latest
Security Latest
AWS News Blog
AWS News Blog
Apple Machine Learning Research
Apple Machine Learning Research
GbyAI
GbyAI
Engineering at Meta
Engineering at Meta
阮一峰的网络日志
阮一峰的网络日志
罗磊的独立博客
F
Full Disclosure
S
Schneier on Security
L
LangChain Blog
MyScale Blog
MyScale Blog
Know Your Adversary
Know Your Adversary
P
Privacy International News Feed
Google Online Security Blog
Google Online Security Blog
Scott Helme
Scott Helme
Stack Overflow Blog
Stack Overflow Blog
爱范儿
爱范儿
A
Arctic Wolf
Martin Fowler
Martin Fowler
B
Blog RSS Feed
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
The Register - Security
The Register - Security
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
博客园_首页
Latest news
Latest news
F
Fortinet All Blogs
G
GRAHAM CLULEY
T
The Exploit Database - CXSecurity.com
Hacker News: Ask HN
Hacker News: Ask HN

overreacted — A blog by Dan Abramov

There Are No Instances in atproto — overreacted Algebraic Effects for the Rest of Us — overreacted A Social Filesystem Introducing RSC Explorer — overreacted Hire Me in Japan — overreacted How to Fix Any Bug — overreacted Where It's at:// — overreacted Open Social A Lean Syntax Primer — overreacted Beyond Booleans — overreacted The Math Is Haunted — overreacted Suppressions of Suppressions — overreacted I'm Doing a Little Consulting — overreacted How Imports Work in RSC — overreacted RSC for LISP Developers — overreacted Progressive JSON — overreacted Why Does RSC Integrate with a Bundler? — overreacted One Roundtrip Per Navigation — overreacted Static as a Server — overreacted Functional HTML — overreacted What Does "use client" Do? — overreacted Impossible Components JSX Over The Wire React for Two Computers The Two Reacts — overreacted A Chain Reaction — overreacted npm audit: Broken by Design — overreacted Before You memo() — overreacted The WET Codebase — overreacted Goodbye, Clean Code — overreacted My Decade in Review — overreacted What Are the React Team Principles? — overreacted On let vs const — overreacted What Is JavaScript Made Of? — overreacted How Does the Development Mode Work? — overreacted Preparing for a Tech Talk, Part 3: Content — overreacted Name It, and They Will Come — overreacted Writing Resilient Components — overreacted A Complete Guide to useEffect How Are Function Components Different from Classes? — overreacted Coping with Feedback — overreacted Fix Like No One’s Watching — overreacted Making setInterval Declarative with React Hooks — overreacted React as a UI Runtime Why Isn’t X a Hook? — overreacted The “Bug-O” Notation — overreacted Preparing for a Tech Talk, Part 2: What, Why, and How — overreacted The Elements of UI Engineering — overreacted Things I Don’t Know as of 2018 — overreacted Preparing for a Tech Talk, Part 1: Motivation — overreacted Why Do React Hooks Rely on Call Order? — overreacted Optimized for Change — overreacted How Does setState Know What to Do? — overreacted My Wishlist for Hot Reloading — overreacted Why Do React Elements Have a $$typeof Property? — overreacted How Does React Tell a Class from a Function? — overreacted Why Do We Write super(props)? — overreacted
RSC for Astro Developers — overreacted
2025-05-06 · via overreacted — A blog by Dan Abramov

Okay, so in Astro you have two things:

  • Astro Components: They have the .astro extension. They execute exclusively on the server or during the build. In other words, their code is never shipped to the client. So they can do things that client code cannot do—read from the filesystem, hit the internal services, even read from a database. But they can’t do interactive things aside from whatever exists natively in the HTML or your own <script>. Astro Components can render either other Astro Components or Client Islands.
  • Client Islands: Components written for React, Vue, and so on. This is your typical frontend stuff. That’s where it’s convenient to add the interactive bits. These Client Islands can then render other components for the same framework using that framework’s own mechanism. So, a React component can render another React component, as you would expect. But you can’t render an Astro Component from a Client Island. That wouldn’t make sense—by that point, Astro already ran.

Here’s a PostPreview.astro Astro Component rendering a LikeButton Island:

---
import { readFile } from 'fs/promises';
import { LikeButton } from './LikeButton';
 
const { slug } = Astro.props;
const title = await readFile(`./posts/${slug}/title.txt`, 'utf8');
---
<article>
  <h1>{title}</h1>
  <LikeButton client:load />
</article>
import { useState } from 'react';
 
export function LikeButton() {
  const [liked, setLiked] = useState(false);
  return (
    <button onClick={() => setLiked(!liked)}>
      {liked ? '❤️' : '🤍'} Like
    </button>
  );
}

Notice how Astro Components and Client Islands essentially live in two different “worlds”, and the data only ever flows down. Astro Components are where all the preprocessing happens; they “hand off” the interactive bits to the Client Islands.

Now let’s look at React Server Components (RSC).

In RSC, the same two things are called Server Components and Client Components. Here is how you’d write the above Astro Component as a React Server Component:

import { readFile } from 'fs/promises';
import { LikeButton } from './LikeButton';
 
async function PostPreview({ slug }) {
  const title = await readFile(`./posts/${slug}/title.txt`, 'utf8');
  return (
    <article>
      <h1>{title}</h1>
      <LikeButton />
    </article>
  );
}
'use client';
 
import { useState } from 'react';
 
export function LikeButton() {
  const [liked, setLiked] = useState(false);
 
  return (
    <button onClick={() => setLiked(!liked)}>
      {liked ? '❤️' : '🤍'} Like
    </button>
  );
}

The mental model behind these two are remarkably similar! If you know Astro, you already have 80% of the mental model for React Server Components. (Even if you think React Server Components are a terrible idea, Astro is worth learning.)

Let’s note a few syntactic differences you might have noticed above:

  • Unlike Astro Components, React Server Components are regular JavaScript functions. They are not “single-file”. The props are coming from the function argument rather than from Astro.props, and there is no separate “template”.
  • In Astro, the separation between Astro Components and Client Islands is achieved by writing the former as .astro files. Once you import a Client Island, you’re not in an .astro file anymore and thus you’re “leaving” the Astro world. In RSC, the same purpose is achieved by the 'use client' directive. The 'use client' directive marks where the Server world “ends”—it is a door between the worlds.
  • In Astro, there are directives like client:load that let you treat Islands either as static HTML or as hydratable on the client. React Server Components does not expose this distinction to the user code. From React’s perspective, if a component was written to be interactive, it would be a mistake to remove this interactivity. If a component truly does not require interactivity, just remove 'use client' from it, and then importing it from the Server world would already keep it Server-only.

The last point is interesting. In Astro, the different syntax (.astro files vs Client Islands) creates a sharp and obvious visual distinction between the two worlds. The same component can’t act as both an Astro Component and a Client Island depending on the context—they’re two distinct things with distinct syntaxes.

But in RSC, the “Astro” part is also “just React”. So if you have a component that doesn’t use any client-specific or server-specific features, it could play either role.

Consider a <Markdown /> component that does its own parsing. Since it doesn’t use any client features (no State) or any server features (no reading DB), it could be imported on either side. So if you import it from a Server world, it’ll act like an “Astro Component”, but if you import it from a Client world, it’ll act like a “Client Island”. This isn’t some new concept, it’s just how importing functions works!

In RSC, stuff imported from the Server world will run in the Server world; stuff that’s imported from the Client world will run in the Client world; and stuff that’s not supported in either world (e.g. DB on the Client or useState on the Server) will cause a build error so you’ll be forced to “cut a door” with 'use client'.

This is both a blessing and a curse.

It is a curse because it makes learning to wield RSC rather unintuitive. You keep worrying about “which world you’re in”. It takes practice to embrace that it doesn’t matter because you can always reason locally. You can use server features like DB in files that need them, use client features like State in files that need them, and rely on build-time assertions causing errors if something is wrong. Then you look at the module stack trace and decide where to “cut a new door” for your “islands”.

This is a curse, but it is also a blessing. By embracing React on both sides, the RSC model solves some Astro limitations that you might encounter along the way:

  • Sometimes, you might write a bunch of Astro Components and later realize that you’re gonna need to move that UI into Client Islands (tweaking the syntax along the way) or even duplicate it because some dynamic UI also wants to drive them. With RSC, you can extract the shared parts and import them from both sides. It is less important to think through “this part will mostly be dynamic” or “this part will mostly be static” for every piece of UI because you can always add or remove 'use client' and/or move it up or down the import chain with little friction. You do decide where to “cut the door”, but there’s no “converting” back and forth.
  • In Astro, you can nest Astro Components inside Client Islands, but if those include more Client Islands, they’ll still be seen as separate roots by your framework (e.g. React). This is why nesting interactive behavior doesn’t compose as naturally as in client apps, e.g. React or Vue context can’t be passed between Astro islands. In RSC, this is not a problem—the entire UI is a single React tree under the hood. You can have a Client context provider above some Server subtree, and then a bunch of Client components reading that context anywhere below. RSC is fractal islands.
  • Astro Components can ultimately produce only HTML. This is why clicking links on an Astro site requires the browser to fully reload the page. If that seems like acceptable UX for your use case, that’s great! You can improve it with manual logic and with View Transitions but fundamentally, the page’s HTML does get replaced. If you want a SPA-like navigation that always keeps the state of the nav chrome, whether any React state or DOM state like inputs and scroll positions, then RSC can fill that gap. RSC uses a JSON-like format for React trees—which can be turned into HTML (for the first paint) but also gets refetched as JSON on navigations. In other words, RSC lets you think in an MPA mental model—but it feels like a SPA.
  • This also means that unlike with Astro, the Server parts of RSC UI are refreshable in-place. If you do actually run a server (and not just running RSC during the build like I do for my blog), RSC lets you “refresh” the screen at any time to let the fresh server props flow into your already existing stateful client-side tree. For example, if some Astro Component needs to refresh in response to an interaction, you would have to choose between a full page refresh or moving logic to a Client Island. In RSC, you can just ask the fresh JSX from the server to get merged into the tree.

In Astro, the fundamental output format is HTML. Since frontend frameworks don’t fundamentally operate the HTML itself (they operate a stateful DOM that can be initialized with HTML), Astro follows a “one-time handoff” model. This makes it arguably easier to learn but limits server features to what the “first render” (to HTML) needs and mostly leaves you on your own with the interactive bits. As you make more things interactive, you might feel yourself running into Astro model’s limitations, possibly choosing to move more logic to SPA-like but isolated Islands.

In RSC, the fundamental output format is a React tree (which can be turned to HTML, but can also be (re)fetched as JSON). Since RSC uses React on both sides with no visual distinction between the two worlds, it is more challenging to learn to wield it. The upside is that once you get the hang of moving the boundaries, they become very fluid and solve the problem where you have to move code “into Astro” or “back into Islands” because something ended up more static or dynamic than expected. You also can retain the same “just map the data to UI” mental model whether the UI is read-only or needs to refetch in response to mutations. The Server parts grow deeper into the tree—interleaved with their Client parts.

And because it’s React on both sides, all React features are integrated end-to-end: for example, a <Suspense> declarative loading state on the Client will “know” to wait for async data (from the Server), JS and CSS (as the Client loads them), fonts and images (with reasonable timeouts), and even trigger the View Transitions (see here). In React, every feature is designed so that the Server and Client pieces are arbitrarily nestable, composable, and refreshable in-place. It’s a single tree. The downside is that buying into RSC means buying into React. RSC is full-stack React.

Finally, it is worth noting that Astro is a framework, but RSC itself is lower-level—think of it as a building block for a framework, or a standard that a framework can implement. The two officially supported implementations of RSC right now include Next.js App Router (a framework) and Parcel RSC (not a framework).

Personally, I think that the developer experience with RSC is still somewhat raw, but I also think you might want to learn it anyway. It has some interesting ideas.

Also, if you’ve never used Astro, give it a try! If RSC is giving you a hard time, Astro might offer a gentler onramp to the same ideas. And if you’ve only ever used client-side React, Astro might solve some problems you never realized you had.