Astroコンポーネント

Astroコンポーネントは、あらゆるAstroプロジェクトの基本的な構成要素です。クライアントサイドのランタイムを持たない、HTMLのみのテンプレートコンポーネントです。

HTMLをご存知の方なら、すでに最初のAstroコンポーネントを書くのに十分な知識を持っています。

Astroコンポーネントの構文は、HTMLのスーパーセットです。この構文は、HTMLやJSXを書いたことのある人なら誰でも親しみやすいように設計されています。また、コンポーネントとJavaScript式を含むためのサポートも追加されています。Astroコンポーネントは、ファイル拡張子が.astroなので、すぐ見分けられます。

Astroコンポーネントは非常に柔軟です。多くの場合、Astroコンポーネントは、ヘッダーやプロフィールカードのような、ページ上で再利用可能なUIを含むことになります。また、Astroコンポーネントには、SEO対策を容易にする共通の<meta>タグのコレクションのような、小さなHTMLのスニペットが含まれることもあります。Astroコンポーネントは、ページ全体のレイアウトを含められます。

Astroコンポーネントについて知っておくべきもっとも重要なことは、ビルド中にHTMLへ変換されることです。コンポーネントの内部でJavaScriptコードを実行しても、すべて事前に実行され、ユーザーに送られる最終ページからは取り除かれます。その結果、デフォルトでは、追加されるJavaScriptの痕跡のない、より高速なサイトが実現します。

Astroコンポーネントは、コンポーネントスクリプトコンポーネントテンプレートという2つの主要な部分で構成されています。それぞれのパーツは異なる仕事を行いますが、この2つを組み合わせることで、使いやすさと、どんなものにも対応できる表現力を兼ね備えたフレームワークを提供することを目指しています。

src/components/EmptyComponent.astro
---
// コンポーネントスクリプト (JavaScript)
---
<!-- コンポーネントテンプレート (HTML + JS Expressions) -->

コンポーネントを他のコンポーネントの内部で使用し、より高度なUIを構築できます。たとえば、Buttonコンポーネントを使用して、ButtonGroupコンポーネントを作成すると、次のようになります。

src/components/ButtonGroup.astro
---
// 例: ButtonGroup.astro
import Button from './Button.astro';
---
<div>
  <Button title="Button 1" />
  <Button title="Button 2" />
  <Button title="Button 3" />
</div>

コンポーネントスクリプト

Section titled コンポーネントスクリプト

Astroでは、Astroコンポーネント内のコンポーネントスクリプトを識別するためにコードフェンス(---)を使用します。Markdownを書いたことがある方なら、すでにフロントマターという同様の概念に馴染みがあるかもしれません。Astroのコンポーネントスクリプトの考え方は、この概念から直接着想を得ています。

コンポーネントスクリプトを使用して、テンプレートをレンダリングするために必要なあらゆるJavaScriptコードを記述できます。

  • 他のAstroコンポーネントのインポート
  • 他のフレームワークコンポーネント(Reactなど)のインポート
  • データ(JSONファイルなど)のインポート
  • APIやデータベースからコンテンツを取得するコード
  • テンプレートで参照する変数の作成
src/components/MyComponent.astro
---
import SomeAstroComponent from '../components/SomeAstroComponent.astro';
import SomeReactComponent from '../components/SomeReactComponent.jsx';
import someData from '../data/pokemon.json';

// 渡されたコンポーネントのprops(`<X title="Hello, World" />`など)にアクセスする。
const {title} = Astro.props;
// 外部データを取得する(プライベートAPIやデータベースからでも可)
const data = await fetch('SOME_SECRET_API_URL/users').then(r => r.json());
---
<!-- テンプレートはここに書きます -->

コードフェンスは、その中に書かれたJavaScriptを「囲い込む」ことを保証するために設計されています。コードがフロントエンドのアプリケーションに漏れたり、ユーザーの手に渡ったりしません。高コストなコードや機密性の高いコード(プライベートなデータベースの呼び出しなど)が、ユーザーのブラウザに届くことを心配せずに、安全にコードを書けます。

コンポーネントテンプレート

Section titled コンポーネントテンプレート

コンポーネントスクリプトの下には、コンポーネントテンプレートがあります。コンポーネントテンプレートは、コンポーネントの出力するHTMLを決定します。

ここにプレーンなHTMLを書けば、そのコンポーネントはAstroのページでインポートされて使用される際にそのHTMLをレンダリングします。

ただし、Astroのコンポーネントテンプレート構文は、JavaScript式インポートしたコンポーネント特別なAstroディレクティブ (EN)もサポートしています。コンポーネントスクリプトで(ページ構築時に)定義されたデータと値をコンポーネントテンプレートで使用することで、動的に作成されたHTMLを生成できます。

src/components/MyFavoritePokemon.astro
---
// コンポーネントスクリプトはここに書きます
import ReactPokemonComponent from '../components/ReactPokemonComponent.jsx';
const myFavoritePokemon = [/* ... */];
---
<!-- HTMLコメントに対応しています -->

<h1>Hello, world!</h1>

<!-- propsやコンポーネントスクリプトの変数を使用します -->
<p>好きなポケモンは: {Astro.props.title}</p>

<!-- `client:`ディレクティブの付いたコンポーネントはハイドレートされます -->
<ReactPokemonComponent client:visible />

<!-- JSXと同じように、HTMLとJavaScriptの式を混ぜられます -->
<ul>
  {myFavoritePokemon.map((data) => <li>{data.name}</li>)}
</ul>

<!-- テンプレートディレクティブを使って、複数の文字列やオブジェクトからクラス名を作成できます -->
<p class:list={["add", "dynamic", {classNames: true}]} />

Astroコンポーネントのフロントマターのコンポーネントスクリプト内で、ローカルJavaScript変数を定義できます。また、JSXに似た式を使用して、これらの変数をコンポーネントのHTMLテンプレートに挿入できます。

ローカル変数は、中括弧({})で囲んで使うことで、HTMLに追加できます。

src/components/Variables.astro
---
const name = "Astro";
---
<div>
  <h1>Hello {name}!</h1>  <!-- 出力: <h1>Hello Astro!</h1> -->
</div>

ローカル変数は、中括弧で囲んで、HTML要素やコンポーネントに属性の値を渡せます。

src/components/DynamicAttributes.astro
---
const name = "Astro";
---
<h1 class={name}>属性式がサポートされています</h1>

<MyComponent templateLiteralNameAttribute={`MyNameIs${name}`} />

ローカル変数は、JSXのような関数で使用でき、動的に生成されたHTML要素を生成できます。

src/components/DynamicHtml.astro
---
const items = ["犬", "猫", "カモノハシ"];
---
<ul>
  {items.map((item) => (
    <li>{item}</li>
  ))}
</ul>

AstroはJSXの論理演算子や三項演算子を用いて、HTMLを条件付きで表示することができます。

src/components/ConditionalHtml.astro
---
const visible = true;
---
{visible && <p>私を見て!</p>}

{visible ? <p>私を見て!</p> : <p>または私を見て!</p>}

HTMLタグ名、またはインポートしたコンポーネントを変数に設定することで動的なタグが利用できます。

src/components/DynamicTags.astro
---
import MyComponent from "./MyComponent.astro";
const Element = 'div'
const Component = MyComponent;
---
<Element>こんにちは!</Element> <!-- <div>こんにちは!</div> としてレンダリングされます -->
<Component /> <!-- <MyComponent />としてレンダリングされます -->

動的なタグを使用する場合:

  • 変数名をキャピタライズする 例えば、elementではなくてElementを利用します。そうでなければ、Astroは変数名をリテラルなHTMLタグとしてレンダリングします。

  • ハイドレーションのディレクティブはサポートしません. client:* ハイドレーションディレクティブを利用する場合、Astroは本番用にバンドルするコンポーネントを知る必要がありますが、動的なタグパターンではこれが動作しません。

フラグメントと複数要素

Section titled フラグメントと複数要素

Astroコンポーネントテンプレートは複数の要素をレンダリングできますが、JavaScriptやJSXとは異なり、その際に全体を1つの<div><>で囲む必要はありません。

src/components/RootElements.astro
---
// 複数の要素を含むテンプレート
---
<p>各要素を1つのコンテナ要素で包む必要はありません。</p>
<p>Astroはテンプレート内の複数のルート要素をサポートします。</p>

しかし、式を使用して複数の要素を動的に作成する場合は、JavaScriptやJSXと同様に、これらの要素をフラグメントで囲む必要があります。Astroでは、<Fragment> </Fragment>または省略形の<> </>のいずれかを使用できます。

src/components/FragmentWrapper.astro
---
const items = ["犬", "猫", "カモノハシ"];
---
<ul>
  {items.map((item) => (
    <>
      <li>赤い{item}</li>
      <li>青い{item}</li>
      <li>緑の{item}</li>
    </>
  ))}
</ul>

また、以下の例のように、set:* ディレクティブ (EN)を追加する際に、ラッパー要素を避けるためにフラグメントが役に立つことかもしれません。

src/components/SetHtml.astro
---
const htmlString = '<p>Raw HTML content</p>';
---
<Fragment set:html={htmlString} />

Astroコンポーネントの構文は、HTMLのスーパーセットです。HTMLやJSXの経験がある人なら誰でも親しみやすいように設計されていますが、.astroファイルとJSXの間には、いくつかの重要な違いがあります。

Astroでは、JSXで使用されているcamelCaseの代わりに、すべてのHTML属性に標準的なkebab-caseフォーマットを使用します。これは、Reactでサポートされていないclassに対しても有効です。

example.astro
<div className="box" dataValue="3" />
<div class="box" data-value="3" />

Astroでは標準的なHTMLのコメントかJavaScript形式のコメントを使用できます。

example.astro
---
---
<!-- .astroファイルではHTMLコメント構文が有効です -->
{/* JSコメント構文も有効です */}

Astroコンポーネントは、propsを定義し、受け取れます。propsは、HTMLをレンダリングするためにコンポーネントテンプレートで利用できます。propsは、フロントマタースクリプトのグローバルな Astro.props で利用できます。

以下は、greetingnameのpropsを受け取るコンポーネントの例です。受け取るpropsは、グローバルな Astro.props オブジェクトから分割代入されることに注意してください。

src/components/GreetingHeadline.astro
---
// 使い方: <GreetingHeadline greeting="Howdy" name="Partner" />
const { greeting, name } = Astro.props;
---
<h2>{greeting}{name}</h2>

このコンポーネントをインポートして、他のAstroコンポーネント、レイアウト、ページでレンダリングする場合、属性としてこれらのpropsを渡せます。

src/components/GreetingCard.astro
---
import GreetingHeadline from './GreetingHeadline.astro';
const name = "Astro"
---
<h1>グリーティングカード</h1>
<GreetingHeadline greeting="やぁ" name={name} />
<p>素敵な一日をお過ごしください!</p>

TypeScriptのProps型のインターフェイスでpropsを定義できます。Astroはフロントマター内のPropsインターフェイスを自動的に検出し、型の警告やエラーを出します。propsは、Astro.propsから分割代入する際に、デフォルト値を与えることもできます。

src/components/GreetingHeadline.astro
---
interface Props {
  name: string;
  greeting?: string;
}

const { greeting = "こんにちは", name } = Astro.props;
---
<h2>{greeting}, {name}!</h2>

コンポーネントのpropsは、何も提供されない場合に使用するデフォルト値を指定できます。

src/components/GreetingHeadline.astro
---
const { greeting = "こんにちは", name = "宇宙飛行士" } = Astro.props;
---
<h2>{greeting}{name}</h2>

<slot />要素は外部HTMLコンテンツのプレースホルダーで、他のファイルからコンポーネントテンプレートに子要素を注入(はめ込む=スロット)できます。

デフォルトでは、コンポーネントに渡されたすべての子要素は、その<slot />内でレンダリングされます。

src/components/Wrapper.astro
---
import Header from './Header.astro';
import Logo from './Logo.astro';
import Footer from './Footer.astro';

const { title } = Astro.props
---
<div id="content-wrapper">
  <Header />
  <Logo />
  <h1>{title}</h1>
  <slot />  <!-- 子要素はここに入ります -->
  <Footer />
</div>
src/pages/fred.astro
---
import Wrapper from '../components/Wrapper.astro';
---
<Wrapper title="Fred's Page">
  <h2>フレッドについて</h2>
  <p>ここでは、フレッドについて紹介します。</p>
</Wrapper>

このパターンはAstroレイアウトコンポーネントの基本です。HTMLコンテンツのページ全体を<Layout></Layout>タグで囲んでレイアウトコンポーネントに送り、共通のページ要素の中にレンダリングさせられます。

Astroコンポーネントは、名前付きスロットも使えます。これを利用すると、対応するスロット名を持つHTML要素のみをスロットの場所に渡せます。

src/components/Wrapper.astro
---
import Header from './Header.astro';
import Logo from './Logo.astro';
import Footer from './Footer.astro';

const { title } = Astro.props
---
<div id="content-wrapper">
  <Header />
  <slot name="after-header"/>  <!--  `slot="after-header"` 属性を持つ子要素はここに入ります。 -->
  <Logo />
  <h1>{title}</h1>
  <slot />  <!--  `slot`属性をもたない子要素、`slot="default"`属性を持つ子要素はここに入ります。 -->
  <Footer />
  <slot name="after-footer"/>  <!--  `slot="after-footer"` 属性を持つ子要素はここに入ります。 -->
</div>
src/pages/fred.astro
---
import Wrapper from '../components/Wrapper.astro';
---
<Wrapper title="フレッドのページ">
  <img src="https://my.photo/fred.jpg" slot="after-header">
  <h2>フレッドについて</h2>
  <p>ここでは、フレッドについて紹介します。</p>
  <p slot="after-footer">Copyright 2022</p>
</Wrapper>

子要素の slot="my-slot" 属性を使用して、コンポーネント内の <slot name="my-slot" /> にマッチするプレースホルダに渡します。

スロットのフォールバックコンテンツ

Section titled スロットのフォールバックコンテンツ

スロットは、フォールバックコンテンツをレンダリングすることもできます。スロットに渡される子要素がない場合、 <slot /> 要素はそれ自身のプレースホルダーの子要素をレンダリングします。

src/components/Wrapper.astro
---
import Header from './Header.astro';
import Logo from './Logo.astro';
import Footer from './Footer.astro';

const { title } = Astro.props
---
<div id="content-wrapper">
  <Header />
  <Logo />
  <h1>{title}</h1>
  <slot>
    <p>これは、スロットに渡された子要素がない場合の代替コンテンツです。</p>
  </slot>
  <Footer />
</div>

CSSの <style> タグも、コンポーネントテンプレートの内部でサポートされています。

これらのタグはコンポーネントのスタイル設定に使えます。すべてのスタイルルールはそのコンポーネントに自動的にスコープが作られ、大規模なアプリでのCSSのコンフリクトを防げます。

src/components/StyledHeading.astro
---
// コンポーネントスクリプトはここに書きます
---
<style>
  /* コンポーネントでスコープが作られ、ページ上の他のh1要素には影響しません */
  h1 { color: red }
</style>

<h1>Hello, world!</h1>

📚 スタイルの適用に関する詳細は、スタイリングガイドを参照してください。

クライアントサイドスクリプト

Section titled クライアントサイドスクリプト

Astroコンポーネントでは、標準的なHTMLの<script>タグを使用してクライアントサイドにインタラクティビティを追加することができます。

スクリプトは、イベントリスナーの追加、アナリティクスデータの送信、アニメーションの再生など、JavaScriptがWeb上で実行できるあらゆることに使用できます。

src/components/ConfettiButton.astro
<button data-confetti-button>お祝いする</button>

<script>
  // npmモジュールをインポートする。
  import confetti from 'canvas-confetti';

  // ページ内のコンポーネントDOMを見つける。
  const buttons = document.querySelectorAll('[data-confetti-button]');

  // ボタンがクリックされたときに紙吹雪(confetti)を発生させるイベントリスナーを追加します。
  buttons.forEach((button) => {
    button.addEventListener('click', () => confetti());
  });
</script>

Astroはデフォルトで<script>タグを処理・バンドルし、npmモジュールのインポートやTypeScriptの記述などに対応しています。

📚 詳細についてはスクリプトのガイドを参照してください。

Astroは.htmlファイルをコンポーネントとしてインポートして使用したり、これらのファイルをページとしてsrc/pagesのサブディレクトリに設置することをサポートしています。フレームワークなしで構築された既存のサイトからコードを再利用したい場合や、コンポーネントに動的な機能が確実に入らないようにしたい場合はHTMLコンポーネントを利用するといいでしょう。

HTMLコンポーネントは有効なHTMLしか含むことができず、そのためAstroコンポーネントの主要機能が制限されます。

  • フロントマターやサーバーサイドのインポート、動的な記法をサポートしません
  • すべての<script>タグはバンドルされずに残され、is:inlineを持つ場合と同じように扱われます。
  • public/フォルダにあるアセットのみを参照できます。

📚 Astroの組み込みコンポーネント (EN)について学びます。

📚 AstroプロジェクトでのJavaScriptフレームワークコンポーネントの使用方法について学びます。