
اگر اسم React Server Components (که به اختصار RSC نامیده میشود) را شنیدهاید و فکر کردهاید «این که همون Server-Side Rendering قدیمیه»، تنها نیستید. این یکی از رایجترین برداشتهای اشتباه در دنیای React است. RSC از React 19 به صورت پایدار (stable) عرضه شده و امروز به مدل پیشفرض رندر در بیشتر فریمورکهای مبتنی بر React تبدیل شده است. در این مقاله بدون وابسته کردن توضیحات به یک فریمورک خاص، بررسی میکنیم که RSC واقعاً چیست، چطور کار میکند و مرز بین کامپوننتهای سرور و کلاینت کجاست.
یک Server Component کامپوننتی است که فقط و فقط در سرور رندر میشود و کد آن هرگز به مرورگر ارسال نمیشود. همین نکته، تفاوت اصلی RSC با SSR سنتی است. در SSR، سرور برای بارگذاری اولیه صفحه، HTML تولید میکند، اما جاوااسکریپت همان کامپوننت هم برای انجام hydration به کلاینت فرستاده میشود. در RSC اما، منطق، importها و وابستگیهای کامپوننت برای همیشه روی سرور باقی میمانند؛ کلاینت هرگز آن را دریافت، پارس یا اجرا نمیکند.
نتیجه این است که یک Server Component میتواند بدون نگرانی از حجم باندل، مستقیماً یک کلاینت دیتابیس را import کند، به متغیرهای محیطی حساس دسترسی داشته باشد یا از یک کتابخانهی پردازشی سنگین استفاده کند؛ چون هیچکدام از اینها وارد باندل جاوااسکریپت کلاینت نمیشوند. تنها چیزی که به کلاینت میرسد، «نتیجهی» رندر آن کامپوننت است که به شکلی فشرده و قابل استریم سریالایز شده است.
در یک اپلیکیشن مبتنی بر RSC، هر کامپوننت در یکی از این دو دسته قرار میگیرد:
useState، useEffect) استفاده کند، اما به منابع سمت سرور مستقیماً دسترسی دارد."use client" در ابتدای فایل مشخص میشود، در مرورگر رندر میشود، از hookها و تعاملپذیری پشتیبانی میکند و مثل کامپوننتهای سنتی React باندل و ارسال میشود.
قانونی که بیشتر از همه باعث سردرگمی میشود این است: یک Server Component میتواند یک Client Component را import و رندر کند، اما برعکسش امکانپذیر نیست. اگر یک Client Component به محتوای رندرشده در سرور نیاز داشته باشد، آن محتوا باید بهعنوان prop (معمولاً از طریق children) به آن پاس داده شود. این محدودیت اتفاقی نیست؛ چون بهمحض عبور به قلمرو کلاینت، هر چیزی در ادامهی آن باید چیزی باشد که مرورگر واقعاً بتواند اجرا کند.
وقتی یک Server Component رندر میشود، React مستقیماً HTML تولید نمیکند. در عوض، یک توصیف سریالایزشده از درخت کامپوننتها را با یک فرمت داخلی به نام پروتکل «Flight» میسازد. این payload مشخص میکند چه چیزی باید رندر شود و شامل ارجاع به Client Componentهایی است که باید hydrate شوند، اما هیچکدام از کد منبع Server Component را در خود ندارد.
سرور میتواند این payload را بهصورت تکهتکه و همزمان با آمادهشدن دادهها استریم کند؛ به همین دلیل RSC بهطور طبیعی با Suspense هماهنگ است. یک فراخوانی کند داده در یک Server Component، بقیهی صفحه را بلاک نمیکند؛ کلاینت هر تکه را به محض رسیدن رندر میکند، نه لزوماً به ترتیب بالا به پایین.

اگر اپلیکیشن از SSR یا رندر استاتیک هم استفاده کند، همان payload برای تولید HTML واقعی در اولین نمایش صفحه استفاده میشود؛ یعنی کاربر محتوای معنادار را بلافاصله میبیند، حتی پیش از بارگذاری هر جاوااسکریپت سمت کلاینت.
Server Componentها برای این موارد مناسباند:
اما این موارد در آنها ممکن نیست:
useState، useEffect، useContext یا هر hook دیگری که به رانتایم کلاینت وابسته استwindow، localStorage یا event listenerهاDate بهعنوان prop به Client Component؛ اینها قابل سریالایز شدن روی مرز سرور/کلاینت نیستند و باعث خطای رانتایم میشوند// Server Component — بدون نیاز به دستورالعمل "use client"
async function ProductList() {
const products = await db.query('SELECT * FROM products');
return (
<ul>
{products.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</ul>
);
}توجه کنید که خود کامپوننت async است و مستقیماً داخل بدنهی تابع منتظر داده میماند؛ نه useEffectای در کار است، نه مدیریت دستی state لودینگ. این فقط به این دلیل ممکن است که این کامپوننت هرگز در مرورگر اجرا نمیشود.
Client Componentها همچنان به راهی برای فعالسازی منطق سمت سرور نیاز دارند؛ مثل ارسال یک فرم یا تغییر یک رکورد در دیتابیس. این کار از طریق Server Function انجام میشود که با دستورالعمل "use server" تعریف میشود. یک Server Function میتواند داخل یک Server Component ساخته و بهعنوان prop پاس داده شود، یا مستقیماً در یک Client Component ایمپورت شود. وقتی از یک المان تعاملی فراخوانی میشود، React یک درخواست به سرور میفرستد، تابع را اجرا میکند و نتیجه را برمیگرداند؛ بدون نیاز به نوشتن دستی یک API route.
تا اواخر سال ۲۰۲۴ به اینها معمولاً «Server Action» گفته میشد. این اصطلاح الان مشخصاً به Server Functionی اشاره دارد که داخل یک prop به نام action یا در ارسال فرم استفاده میشود؛ یعنی هر Server Function لزوماً Server Action نیست، هرچند هر Server Action یک Server Function است.
"use client" علامتگذاری کردن. این کار بخش زیادی از هدف اصلی RSC را از بین میبرد و در نهایت همان حجم جاوااسکریپتی را ارسال میکنید که قرار بود از آن دوری کنید. پیشفرض را روی Server Component بگذارید و فقط جایی که واقعاً به تعاملپذیری نیاز است، سراغ رندر کلاینت بروید.Date نمیتوانند بهعنوان prop از Server Component به Client Component عبور کنند.RSC دیگر یک قابلیت آزمایشی نیست. در React 19 پایدار شده، به معماری پیشفرض پرکاربردترین فریمورکهای React تبدیل شده و اکوسیستم اطراف آن -از کتابخانههای مدیریت state گرفته تا ابزارهای دریافت داده و راهکارهای استایلدهی- تا حد زیادی خودش را با آن هماهنگ کرده است. سؤال اصلی این نیست که آیا RSC آمادهی محیط تولید است یا نه؛ سؤال این است که آیا استک فنی خاص شما (باندلر، فریمورک، پلتفرم دیپلوی) از آن پشتیبانی کامل میکند یا نه، چون RSC نسبت به یک ویژگی معمولی React، به یکپارچگی عمیقتری با ابزارهای build نیاز دارد. اگر روی فریمورکی کار میکنید که پشتیبانی از RSC را بهصورت آماده ارائه میدهد، دلیل چندانی برای دوری از آن وجود ندارد؛ فقط کاهش حجم جاوااسکریپت سمت کلاینت معمولاً ارزش تغییر مدل ذهنی را دارد.