jpskill.com
🛠️ 開発・MCP コミュニティ

seo-handler

SEO対策として、ウェブサイトの検索順位を上げるために必要なサイトマップ作成やメタデータ管理などを効率的に行い、ウェブサイトを見つけやすくするSkill。

📜 元の英語説明(参考)

Manage SEO, sitemaps, and metadata for optimal search engine visibility

🇯🇵 日本人クリエイター向け解説

一言でいうと

SEO対策として、ウェブサイトの検索順位を上げるために必要なサイトマップ作成やメタデータ管理などを効率的に行い、ウェブサイトを見つけやすくするSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o seo-handler.zip https://jpskill.com/download/16916.zip && unzip -o seo-handler.zip && rm seo-handler.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/16916.zip -OutFile "$d\seo-handler.zip"; Expand-Archive "$d\seo-handler.zip" -DestinationPath $d -Force; ri "$d\seo-handler.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して seo-handler.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → seo-handler フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

SEO Handler Skill

この Skill は、あなたの Next.js アプリケーションが検索エンジン向けに最適化されるようにします。これは、サイトマップ、メタデータ (OpenGraph)、サーバーサイドレンダリング (SSR/ISR)、構造化データ (JSON-LD)、および robots.txt の設定を網羅します。

コア原則

  1. サイトマップの可視性: すべての公開ページは src/app/sitemap.ts に存在しなければなりません。
  2. メタデータの継承: デフォルトには layout.tsx を使用し、page.tsx でオーバーライドします。
  3. 構造化データ: すべてのコンテンツページで next-seo コンポーネント (JSON-LD) を使用します。
  4. サーバーサイドデータ: SEO のために、コンテンツを多く含むデータはサーバー (ISR) でフェッチします。
  5. Robots の制御: クローラーを誘導するために src/app/robots.ts を使用します。

1. サイトマップの管理

ファイル: src/app/sitemap.ts

新しい公開ルート (例: /features) を追加する場合は、それを staticPages 配列に追加します。

const staticPages = [
  "", // Home
  "/features", // New page
  // ...
];

サイトマップの分割 (大規模サイト)

50,000 を超える URL (例: ユーザープロファイル、製品) がある場合は、generateSitemaps を使用します。

ファイル: src/app/products/sitemap.ts

import { BASE_URL } from '@/lib/constants';

export async function generateSitemaps() {
  // 合計数をフェッチし、バッチサイズ (例: 50,000) で割ります
  return [{ id: 0 }, { id: 1 }];
}

export default async function sitemap({ id }: { id: { id: number } }) {
  const start = id.id * 50000;
  const products = await getProducts(start, 50000);

  return products.map(product => ({
    url: `${BASE_URL}/products/${product.slug}`,
    lastModified: product.updatedAt,
  }));
}

2. メタデータと OpenGraph

ベースレイアウト: src/app/layout.tsx (またはグループレイヤーアウト) はテンプレートを定義します。

export const metadata: Metadata = {
  title: {
    template: "%s | My App",
    default: "My App - The Best SaaS",
  },
  description: "...",
  openGraph: { ... }, // Default OG
};

ページレベル: src/app/blog/[slug]/page.tsx

export async function generateMetadata({ params }): Promise<Metadata> {
  const post = await getPost(params.slug);
  return {
    title: post.title,
    description: post.summary,
    openGraph: {
      images: [post.coverImage], // Overrides default
    },
  };
}

3. 構造化データ (JSON-LD)

主要: Next-SEO コンポーネント

JSON-LD 構造化データを注入するには、next-seo コンポーネントを使用します。これは、検索エンジンがコンテンツ (記事、製品、FAQ など) を理解するのに役立ちます。

ページコンポーネント: src/app/blog/[slug]/page.tsx

import { ArticleJsonLd } from "next-seo";

export default function BlogPost({ post }) {
  return (
    <>
      <ArticleJsonLd
        useAppDir
        url={`https://myapp.com/blog/${post.slug}`}
        title={post.title}
        images={[post.image]}
        datePublished={post.date}
        authorName="My App"
        description={post.description}
      />
      <article>...</article>
    </>
  );
}

代替: Raw JSON-LD (スクリプトタグ)

特定のスキーマが next-seo でサポートされていない場合は、raw スクリプトタグを使用します。データフェッチが非同期の場合は、Suspense でラップします。

import { Suspense } from "react";

export default function Page() {
  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'CustomType',
    name: 'Custom Name',
    description: 'Description',
  };

  return (
    <section>
      <Suspense fallback={null}>
        <script
          type="application/ld+json"
          dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
        />
      </Suspense>
      {/* Page Content */}
    </section>
  );
}

4. インクリメンタル静的再生成 (ISR)

データベースコンテンツに依存するコンテンツページ (ブログ、ドキュメント、マーケティング) の場合は、ISR を使用してエッジでキャッシュします。

ページコンポーネント:

// src/app/blog/[slug]/page.tsx

// 1. ISR を有効にする
export const revalidate = 3600; // 1時間ごとに再検証 (秒単位)
// または、完全な静的エクスポート動作のために generateStaticParams で動的パラメータを使用します
export const dynamicParams = true; 

export async function generateStaticParams() {
  const posts = await db.query.posts.findMany();
  return posts.map((post) => ({ slug: post.slug }));
}

export default async function BlogPost({ params }) {
  // 2. サーバーでフェッチ (データは revalidate 設定に基づいてキャッシュされます)
  const post = await getPost(params.slug); 

  return <article>{/* ... */}</article>;
}

5. Robots.txt の設定

ファイル: src/app/robots.ts

クロールバジェットを節約するために、プライベートルート (/api/, /app/, /admin/) を必ず許可しないようにしてください。

export default function robots(): MetadataRoute.Robots {
  return {
    rules: {
      userAgent: "*",
      allow: "/",
      disallow: ["/api/", "/app/", "/super-admin/"],
    },
    sitemap: `${process.env.NEXT_PUBLIC_APP_URL}/sitemap.xml`,
  };
}
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

SEO Handler Skill

This skill ensures your Next.js application is optimized for search engines. It covers sitemaps, metadata (OpenGraph), server-side rendering (SSR/ISR), structured data (JSON-LD), and robots.txt configuration.

Core Principles

  1. Sitemap Visibility: Every public page MUST be in src/app/sitemap.ts.
  2. Metadata Inheritance: Use layout.tsx for defaults; override in page.tsx.
  3. Structured Data: Use next-seo components (JSON-LD) on every content page.
  4. Server-Side Data: Fetch content-heavy data on the server (ISR) for SEO.
  5. Robots Control: Use src/app/robots.ts to guide crawlers.

1. Sitemap Management

File: src/app/sitemap.ts

When adding a new public route (e.g., /features), add it to the staticPages array.

const staticPages = [
  "", // Home
  "/features", // New page
  // ...
];

Splitting Sitemaps (Large Sites)

If you have >50k URLs (e.g., user profiles, products), use generateSitemaps.

File: src/app/products/sitemap.ts

import { BASE_URL } from '@/lib/constants';

export async function generateSitemaps() {
  // Fetch total count and divide by batch size (e.g., 50,000)
  return [{ id: 0 }, { id: 1 }];
}

export default async function sitemap({ id }: { id: { id: number } }) {
  const start = id.id * 50000;
  const products = await getProducts(start, 50000);

  return products.map(product => ({
    url: `${BASE_URL}/products/${product.slug}`,
    lastModified: product.updatedAt,
  }));
}

2. Metadata & OpenGraph

Base Layout: src/app/layout.tsx (or group layout) defines the template.

export const metadata: Metadata = {
  title: {
    template: "%s | My App",
    default: "My App - The Best SaaS",
  },
  description: "...",
  openGraph: { ... }, // Default OG
};

Page Level: src/app/blog/[slug]/page.tsx

export async function generateMetadata({ params }): Promise<Metadata> {
  const post = await getPost(params.slug);
  return {
    title: post.title,
    description: post.summary,
    openGraph: {
      images: [post.coverImage], // Overrides default
    },
  };
}

3. Structured Data (JSON-LD)

Primary: Next-SEO Components

Use next-seo components to inject JSON-LD structured data. This helps search engines understand your content (Articles, Products, FAQs, etc.).

Page Component: src/app/blog/[slug]/page.tsx

import { ArticleJsonLd } from "next-seo";

export default function BlogPost({ post }) {
  return (
    <>
      <ArticleJsonLd
        useAppDir
        url={`https://myapp.com/blog/${post.slug}`}
        title={post.title}
        images={[post.image]}
        datePublished={post.date}
        authorName="My App"
        description={post.description}
      />
      <article>...</article>
    </>
  );
}

Alternative: Raw JSON-LD (Script Tag)

If a specific schema isn't supported by next-seo, use a raw script tag. Wrap in Suspense if the data fetching is async.

import { Suspense } from "react";

export default function Page() {
  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'CustomType',
    name: 'Custom Name',
    description: 'Description',
  };

  return (
    <section>
      <Suspense fallback={null}>
        <script
          type="application/ld+json"
          dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
        />
      </Suspense>
      {/* Page Content */}
    </section>
  );
}

4. Incremental Static Regeneration (ISR)

For content pages (Blogs, Docs, Marketing) that rely on database content, use ISR to cache them at the edge.

Page Component:

// src/app/blog/[slug]/page.tsx

// 1. Enable ISR
export const revalidate = 3600; // Revalidate every hour (in seconds)
// OR use dynamic params with generateStaticParams for full static export behavior
export const dynamicParams = true; 

export async function generateStaticParams() {
  const posts = await db.query.posts.findMany();
  return posts.map((post) => ({ slug: post.slug }));
}

export default async function BlogPost({ params }) {
  // 2. Fetch on Server (Data is cached based on revalidate config)
  const post = await getPost(params.slug); 

  return <article>{/* ... */}</article>;
}

5. Robots.txt configuration

File: src/app/robots.ts

Ensure you disallow private routes (/api/, /app/, /admin/) to save crawl budget.

export default function robots(): MetadataRoute.Robots {
  return {
    rules: {
      userAgent: "*",
      allow: "/",
      disallow: ["/api/", "/app/", "/super-admin/"],
    },
    sitemap: `${process.env.NEXT_PUBLIC_APP_URL}/sitemap.xml`,
  };
}