ข้ามไปยังเนื้อหา

Slots & Composition

component มักต้องห่อ content ที่ยังไม่รู้ล่วงหน้า markup ที่ parent วางไว้ระหว่าง tag ของ component จะถูก render ตรงที่ <slot /> อยู่ — นั่นคือ default slot

src/components/Panel.astro
---
const { heading } = Astro.props;
---
<section class="panel">
<h2>{heading}</h2>
<slot />
</section>
<Panel heading="Notes">
<p>This paragraph lands in the default slot.</p>
</Panel>

default slot อันเดียวไม่พอสำหรับ layout จริง ๆ named slot ให้ช่องที่มีป้ายชื่อหลายช่อง component ประกาศ <slot name="x" /> ส่วน parent เติมช่องเหล่านั้นด้วย slot="x" บน child

src/components/Shell.astro
<header><slot name="header" /></header>
<main><slot /></main> <!-- default slot -->
<footer><slot name="footer" /></footer>
<Shell>
<h1 slot="header">Page title</h1>
<p>Body content fills the default slot.</p>
<small slot="footer">© 2026</small>
</Shell>

<slot> เก็บ content เริ่มต้น ที่จะ render เฉพาะตอน parent ไม่ส่งอะไรมาให้ slot นั้นได้

<button class="btn">
<slot>Click me</slot> <!-- "Click me" shows if no children are passed -->
</button>
<Button /> <!-- renders: Click me -->
<Button>Save</Button> <!-- renders: Save -->

ภายใน component คุณถามได้ว่า slot ได้รับ content หรือไม่ ผ่าน Astro.slots.has แล้ว render โครงสร้างแบบมีเงื่อนไข

---
const hasAside = Astro.slots.has('aside');
---
<div class:list={["layout", { "has-aside": hasAside }]}>
<main><slot /></main>
{hasAside && (
<aside><slot name="aside" /></aside>
)}
</div>

แบบนี้ทำให้ component ปรับ markup ได้ — เช่น render wrapper aside เฉพาะตอนมี aside จะโชว์จริง ๆ เพื่อไม่ให้ปล่อย element ว่างออกมา

นี่คือสิ่งที่ slot มีไว้ เพื่อ ในทางปฏิบัติ layout คือ component ที่นิยาม shell ของหน้า — <html>, <head>, nav, footer — พร้อม <slot /> สำหรับ body ของหน้า ทุกหน้าห่อ content ของตัวเองใน layout ดังนั้น shell อยู่ที่เดียว

src/layouts/BaseLayout.astro
---
const { title } = Astro.props;
---
<html lang="en">
<head>
<meta charset="utf-8" />
<title>{title}</title>
<slot name="head" />
</head>
<body>
<nav>…site nav…</nav>
<main><slot /></main>
<footer>…site footer…</footer>
</body>
</html>
src/pages/about.astro
---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="About">
<link slot="head" rel="canonical" href="https://example.com/about" />
<h1>About us</h1>
<p>Page-specific content in the default slot.</p>
</BaseLayout>
flowchart TB
  layout["BaseLayout: html, head, nav, footer with slots"] --> page["Page fills the default slot with its body"]
  page --> head["Page fills the head slot with meta tags"]
  layout --> out["one composed HTML document"]
  page --> out
  head --> out
layout คือ shell ที่มี slot; หน้าต่าง ๆ เติม slot

ด้วย slot คุณประกอบ component เล็ก ๆ ให้เป็นหน้าได้แบบเดียวกับที่ประกอบฟังก์ชัน: Card ที่มี default slot สำหรับ body, Grid ที่ default slot เก็บ card, Section ที่มี named slot header และ body คุณเก็บแต่ละชิ้นให้โฟกัสแล้วให้ parent ตัดสินใจว่าจะใส่อะไรข้างใน เพราะทั้งหมด render บน server composition แทบไม่มีต้นทุนตอน runtime — เป็นแค่การประกอบ HTML เท่านั้น

คุณทำจุดฉีดมากกว่าหนึ่งจุดใน component ยังไง?
`<slot>Click me</slot>` render อะไรเมื่อ parent ไม่ส่ง children มา?
component เช็คว่า slot ได้รับ content ยังไง?
การใช้งานหลักของ slot ในไซต์ Astro จริง ๆ คืออะไร?