はじめに
AIを使ったフロントエンド開発が当たり前になる中で、「とりあえず動くコード」は誰でも一瞬で生成できるようになりました。しかし、無駄な再レンダリングの防止やアクセシビリティの担保など、実運用に耐えうる「品質の高いコード」をAIに一発で書かせるのは、意外と難しいと感じています。
そんな中、Vercelから「React Best Practices」が発表されました。AIエージェントやLLM向けに最適化された構造化リポジトリです。
私たちは、10年以上にわたるReactとNext.jsの最適化に関する知識をreact-best-practices、AIエージェントとLLM向けに最適化された構造化リポジトリに集約しました。
(Introducing React Best Practices より)
この公式Skillを活用することで、AIが「Vercelのトップエンジニアの知見」を持った状態でコードを書いてくれるようになります。
これを使えば、Reactに不慣れなチームでも、開発がラクになるのでは?と思ったので汎用的なButtonコンポーネントを作って比較してみました。
実践
今回は、フロントエンド開発でも身近である「汎用的なButtonコンポーネント」を例に通常のAI生成コードと、Vercelのスキルを適用したAI生成コードを比較してみました。
※今回の検証環境
- ツール: GitHub Copilot CLI
- モデル: Claude 4.6 Sonnet
インストール
npx skills add vercel-labs/agent-skills
スキル未使用:通常のAIが生成したButton
まずは、特に何もコンテキストを与えずに「汎用的なButtonコンポーネントを作って」と指示した場合のコード(Button1)です。
import type { ButtonHTMLAttributes } from "react";
type Variant = "primary" | "secondary" | "danger";
type Size = "sm" | "md" | "lg";
interface Button1Props extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: Variant;
size?: Size;
}
const variantStyles: Record<Variant, string> = {
primary:
"bg-blue-600 text-white hover:bg-blue-700 active:bg-blue-800 dark:bg-blue-500 dark:hover:bg-blue-600",
secondary:
"bg-gray-100 text-gray-800 hover:bg-gray-200 active:bg-gray-300 dark:bg-gray-700 dark:text-gray-100 dark:hover:bg-gray-600",
danger:
"bg-red-600 text-white hover:bg-red-700 active:bg-red-800 dark:bg-red-500 dark:hover:bg-red-600",
};
const sizeStyles: Record<Size, string> = {
sm: "px-3 py-1.5 text-sm",
md: "px-4 py-2 text-base",
lg: "px-6 py-3 text-lg",
};
function Button({
variant = "primary",
size = "md",
className = "",
disabled,
children,
...props
}: Button1Props) {
return (
<button
{...props}
disabled={disabled}
className={[
"inline-flex items-center justify-center rounded-xl font-medium transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed",
variantStyles[variant],
sizeStyles[size],
className,
].join(" ")}
>
{children}
</button>
);
}
export default Button;
Vercelスキル使用:最適化されたButton
次に、VercelのReact Best Practicesスキルを読み込ませた状態で同じ指示を出してみたコード(Button2)です。
import { memo } from "react";
export type ButtonVariant = "primary" | "secondary" | "outline" | "ghost";
export type ButtonSize = "sm" | "md" | "lg";
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant;
size?: ButtonSize;
children: React.ReactNode;
}
// Hoisted outside component to avoid recreation on each render (rendering-hoist-jsx / rerender-memo-with-default-value)
const BASE =
"inline-flex items-center justify-center font-medium rounded-lg transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50";
const VARIANT_STYLES: Record<ButtonVariant, string> = {
primary:
"bg-blue-600 text-white hover:bg-blue-700 focus-visible:ring-blue-600 dark:bg-blue-500 dark:hover:bg-blue-400",
secondary:
"bg-gray-100 text-gray-900 hover:bg-gray-200 focus-visible:ring-gray-400 dark:bg-gray-800 dark:text-gray-100 dark:hover:bg-gray-700",
outline:
"border border-gray-300 bg-transparent text-gray-700 hover:bg-gray-50 focus-visible:ring-gray-400 dark:border-gray-600 dark:text-gray-200 dark:hover:bg-gray-800",
ghost:
"bg-transparent text-gray-700 hover:bg-gray-100 focus-visible:ring-gray-400 dark:text-gray-200 dark:hover:bg-gray-800",
};
const SIZE_STYLES: Record<ButtonSize, string> = {
sm: "h-8 px-3 text-sm",
md: "h-10 px-4 text-sm",
lg: "h-12 px-6 text-base",
};
// React.memo prevents unnecessary re-renders when parent re-renders (rerender-memo)
const Button = memo(function Button({
variant = "primary",
size = "md",
className = "",
children,
...props
}: ButtonProps) {
const classes = `${BASE} ${VARIANT_STYLES[variant]} ${SIZE_STYLES[size]} ${className}`;
return (
<button className={classes} {...props}>
{children}
</button>
);
});
export default Button;
スキルを使って2つのコンポーネントをレビュー・比較させてみた
実際にこれら2つのコードを、Vercelスキルを持ったAI自身にレビュー・比較してもらいました。
レビュー結果からも分かる通り、単なるスタイリングの違いだけでなく、Reactの設計思想に基づいた明確な差が現れました。
-
素晴らしい点(型の再利用性とアクセシビリティ)
Button2 はPropsだけでなく ButtonVariant 等の型もexportしており、親コンポーネントからの拡張性が考慮されています。また、focus:outline-none によるマウス操作時のフォーカスリング課題を、focus-visible を使って適切に解決しています。 -
賛否両論な点(過剰な最適化)
Button2 は React.memo を使用しています。Vercelスキルはこれを「最適化」として高く評価しますが、汎用的な小さなコンポーネントに対しては「やりすぎ(オーバーエンジニアリング)」の場合があります。親から渡す関数(onClick など)に useCallback を徹底しないと意味をなさず、ただ比較の計算コストだけが増えてしまう可能性があります。また、手動でガチガチに最適化を書くスタイルは、自動最適化を目指すReact 19(React Compiler)の思想とはやや逆行しているようにも感じます
まとめ
「汎用的なButtonコンポーネントを作って」という全く同じシンプルなプロンプトでも、VercelのベストプラクティスをAIにインプットさせておくだけで、生成されるコードの品質に差がありました。
一方で今回の検証を通して、「ゼロからコードを生成させる」だけでなく、「レビュー時のサポート役」として活用するのも面白そうだと感じました。
AIに最初からすべて書かせると、プロジェクトの規模によっては少しオーバースペック(過剰な最適化)に感じるコードが出力されることもあります。そのため、まずは自分たちで書いたシンプルなコードに対して、「アクセシビリティ面で足りない部分はあるか?」「最適化できるポイントはあるか?」をAIにレビューしてもらう、といった使い方も相性が良さそうです。
AIが提示してくれるVercelの高度な知見を参考にしつつ、実際のプロジェクトのメンテナンス性に合わせて、必要な項目を柔軟に取り入れていく。そんな活用法も良いかもしれません。
とても簡単に導入できるので、気になった方はぜひ自身のプロジェクトで試してみてください。
参考


