EmDash は、投稿・固定ページ・タクソノミー・メニュー・ウィジェット・メディアライブラリといったおなじみの WordPress の概念を、モダンな Astro スタックに持ち込みます。コンテンツ管理の知識はそのまま活かせます。
変わらないところ
WordPress で知っている概念は、EmDash では第一級の機能です。
- Collections は Custom Post Types のように動作します—コンテンツ構造を定義し、テンプレートでクエリします
- Taxonomies も同様—階層型(カテゴリのように)とフラット型(タグのように)
- Menus はドラッグ&ドロップの並び替えとネストした項目
- Widget Areas はサイドバーや動的コンテンツ領域向け
- Media library はアップロード・整理・画像管理
- コードに触れずに使える Admin UI
違うところ
実装は変わりますが、頭の中のモデルは同じです。
PHP の代わりに TypeScript
テンプレートは Astro コンポーネントです。構文はすっきりしていますが、考え方は同じ—HTML を出力するサーバー側コードです。
WP_Query の代わりに Content API
getEmDashCollection() のようなクエリ関数が WP_Query の代わりになります。SQL はなく、関数呼び出しだけです。
ファイルベースのルーティング
src/pages/ のファイルが URL になります。リライトルールやテンプレート階層を暗記する必要はありません。
テンプレートパーツの代わりにコンポーネント
コンポーネントを import して使います。get_template_part() と同じ発想で、整理しやすくなります。
早見表
| WordPress | EmDash | メモ |
|---|---|---|
| Custom Post Types | Collections | 管理 UI または API で定義 |
WP_Query | getEmDashCollection() | フィルタ・件数・タクソノミークエリ |
get_post() | getEmDashEntry() | エントリまたは null を返す |
| Categories/Tags | Taxonomies | 階層サポートはそのまま |
register_nav_menus() | getMenu() | 第一級のメニュー対応 |
register_sidebar() | getWidgetArea() | 第一級のウィジェットエリア |
bloginfo('name') | getSiteSetting("title") | サイト設定 API |
the_content() | <PortableText /> | 構造化コンテンツのレンダリング |
| Shortcodes | Portable Text blocks | カスタムコンポーネント |
add_action/filter() | Plugin hooks | content:beforeSave など |
wp_options | ctx.kv | キー・バリュー保存 |
| Theme directory | src/ directory | コンポーネント、レイアウト、ページ |
functions.php | astro.config.mjs + EmDash config | ビルドとランタイム設定 |
Content API
コレクションのクエリ
WordPress は WP_Query やヘルパーでクエリします。EmDash は型付きクエリ関数を使います。
WordPress
<?php
$posts = new WP_Query([
'post_type' => 'post',
'posts_per_page' => 10,
'post_status' => 'publish',
'category_name' => 'news',
]);
while ($posts->have_posts()) :
$posts->the_post();
?>
<h2><?php the_title(); ?></h2>
<?php the_excerpt(); ?>
<?php endwhile; ?> EmDash
---
import { getEmDashCollection } from "emdash";
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
limit: 10,
where: { category: "news" },
});
---
{posts.map((post) => (
<article>
<h2>{post.data.title}</h2>
<p>{post.data.excerpt}</p>
</article>
))} 単一エントリの取得
WordPress
<?php
$post = get_post($id);
?>
<article>
<h1><?php echo $post->post_title; ?></h1>
<?php echo apply_filters('the_content', $post->post_content); ?>
</article> EmDash
---
import { getEmDashEntry } from "emdash";
import { PortableText } from "emdash/ui";
const { slug } = Astro.params;
const { entry: post } = await getEmDashEntry("posts", slug);
## if (!post) return Astro.redirect("/404");
<article>
<h1>{post.data.title}</h1>
<PortableText value={post.data.content} />
</article> テンプレート階層
WordPress はテンプレート階層でどのファイルがページを描画するか選びます。Astro は明示的なファイルベースのルーティングを使います。
| WordPress Template | EmDash での相当 |
|---|---|
index.php | src/pages/index.astro |
single.php | src/pages/posts/[slug].astro |
single-{type}.php | src/pages/{type}/[slug].astro |
page.php | src/pages/pages/[slug].astro |
archive.php | src/pages/posts/index.astro |
archive-{type}.php | src/pages/{type}/index.astro |
category.php | src/pages/categories/[slug].astro |
tag.php | src/pages/tags/[slug].astro |
search.php | src/pages/search.astro |
404.php | src/pages/404.astro |
header.php / footer.php | src/layouts/Base.astro |
sidebar.php | src/components/Sidebar.astro |
テンプレートパーツ → コンポーネント
WordPress のテンプレートパーツは Astro コンポーネントになります。
WordPress
// In template:
get_template_part('template-parts/content', 'post');
// template-parts/content-post.php:
<article class="post">
<h2><?php the_title(); ?></h2>
<?php the_excerpt(); ?>
</article> EmDash
---
const { post } = Astro.props;
---
<article class="post">
<h2>{post.data.title}</h2>
<p>{post.data.excerpt}</p>
</article>---
import PostCard from "../components/PostCard.astro";
import { getEmDashCollection } from "emdash";
const { entries: posts } = await getEmDashCollection("posts");
---
{posts.map((post) => <PostCard {post} />)} メニュー
EmDash は URL 解決が自動の、第一級のメニュー対応があります。
WordPress
<?php
wp_nav_menu([
'theme_location' => 'primary',
'container' => 'nav',
]);
?> EmDash
---
import { getMenu } from "emdash";
## const menu = await getMenu("primary");
<nav>
<ul>
{menu?.items.map((item) => (
<li>
<a href={item.url}>{item.label}</a>
</li>
))}
</ul>
</nav> メニューは管理 UI、シードファイル、または WordPress インポートで作成します。
ウィジェットエリア
ウィジェットエリアは WordPress のサイドバーのように動きます。
WordPress
<?php if (is_active_sidebar('sidebar-1')) : ?>
<aside>
<?php dynamic_sidebar('sidebar-1'); ?>
</aside>
<?php endif; ?> EmDash
---
import { getWidgetArea } from "emdash";
import { PortableText } from "emdash/ui";
## const sidebar = await getWidgetArea("sidebar");
{sidebar && (
<aside>
{sidebar.widgets.map((widget) => {
if (widget.type === "content") {
return <PortableText value={widget.content} />;
}
// Handle other widget types
})}
</aside>
)} サイト設定
サイトオプションやカスタマイザー設定は getSiteSetting() に対応します。
| WordPress | EmDash |
|---|---|
bloginfo('name') | getSiteSetting("title") |
bloginfo('description') | getSiteSetting("tagline") |
get_custom_logo() | getSiteSetting("logo") |
get_option('date_format') | getSiteSetting("dateFormat") |
home_url() | Astro.site |
import { getSiteSetting } from "emdash";
const title = await getSiteSetting("title");
const logo = await getSiteSetting("logo"); // Returns { mediaId, alt, url }
タクソノミー
概念は同じ—階層型(カテゴリのように)かフラット型(タグのように)です。
import { getTaxonomyTerms, getEntryTerms, getTerm } from "emdash";
// Get all categories
const categories = await getTaxonomyTerms("categories");
// Get a specific term
const news = await getTerm("categories", "news");
// Get terms for a post
const postCategories = await getEntryTerms("posts", postId, "categories");
Hooks → プラグインシステム
WordPress のフック(add_action、add_filter)は EmDash のプラグインフックになります。
| WordPress Hook | EmDash Hook | 目的 |
|---|---|---|
save_post | content:beforeSave | 保存前にコンテンツを変更 |
the_content | PortableText components | レンダリング結果を変換 |
pre_get_posts | Query options | クエリをフィルタ |
wp_head | Layout <head> | head にコンテンツを追加 |
wp_footer | Layout before </body> | フッターにコンテンツを追加 |
EmDash が優れている点
型安全
全体で TypeScript。コレクション・クエリ・コンポーネントはすべて型付き。フィールド名や戻り値を推測する必要はありません。
パフォーマンス
PHP のオーバーヘッドなし。デフォルトは静的生成。必要ならサーバーレンダリング。エッジデプロイにも向きます。
モダンな DX
Hot module replacement。コンポーネント指向。Vite、TypeScript、ESLint など現代的なツールチェーン。
Git ベースのデプロイ
コードとテンプレートは git。コンテンツは DB。FTP やファイル権限、乗っ取られたサイトの心配がありません。
プレビューリンク
EmDash は HMAC 署名トークン付きの安全なプレビュー URL を生成します。編集者は本番にログインせず下書きを確認できます—リンクを共有するだけで、認証情報は不要です。
プラグイン競合がない
WordPress のようなプラグイン競合は起きにくくなります。EmDash のプラグインは明示的な API を持つ隔離されたコンテキストで動きます。グローバル状態の汚染がありません。
コンテンツ編集者の体験
編集者は wp-admin に近い EmDash 管理パネルを使います。
- 最近の活動がある Dashboard
- 検索・フィルタ・一括操作がある コレクション一覧
- コンテンツ用の リッチエディタ(Portable Text。Gutenberg ではありません)
- ドラッグ&ドロップアップロードの メディアライブラリ
- ドラッグ&ドロップ並び替えの メニュービルダー
- サイドバーコンテンツ用の ウィジェットエリアエディタ
編集体験は馴染みやすく、その下の技術はモダンです。
移行の流れ
EmDash は WordPress コンテンツを直接インポートします。
- WordPress からエクスポート(ツール → エクスポート)
.xmlを EmDash 管理画面にアップロード- 投稿タイプをコレクションにマッピング
- コンテンツとメディアをインポート
投稿・固定ページ・タクソノミー・メニュー・メディアが移ります。Gutenberg ブロックは Portable Text に変換されます。カスタムフィールドは解析してマッピングされます。
手順の詳細は WordPress 移行ガイド を参照してください。
次のステップ
- Getting Started — 最初の EmDash サイトをセットアップ
- Querying Content — Content API を深く学ぶ
- Taxonomies — カテゴリ、タグ、カスタムタクソノミー
- Menus — ナビゲーションメニュー
- Migrate from WordPress — 既存コンテンツのインポート