ASP.NET MVC で画面の共通部品を作り分ける(部分ビュー / HTML Helper / ViewComponent)
みなさんこんにちは!ヒロポンです!
一覧画面の明細行、検索フォーム、ステータスのバッジ。同じ HTML を画面ごとにコピペして、あとから1箇所直すのに全画面を触るハメになったこと、ありませんか??
ASP.NET MVC には、この「共通部品」を作る手段が3つあります。部分ビュー・HTML Helper・ViewComponent。
この記事は、ASP.NET 部分ビュー 使い分けで迷う人向けに、3つを「何を共通化するのに向くか」で作り分けて整理していきます。全部モデルを渡すだけの部品にすればいい、という話じゃないんですよね。
💡 コードは基本 ASP.NET Core MVC 前提です。
.NET Frameworkの MVC 5 の場合、ViewComponent の代わりに子アクション(後述)を使います。
明細行を毎回コピペしていた話
私も最初は、一覧画面の <tr> を画面ごとにコピペしてました。3画面くらいまでなら、まあ平気なんです。
きついのは、列を1つ足す時。全画面を開いて、同じ <td> を足して回る。1箇所ズレて、あとで「あの画面だけ表示が違う」で呼ばれる。地味に消耗します。
共通部品にすれば、直すのは1ファイル。ただ、どの手段で共通化するかを間違えると、逆に読みにくくなるんですよね。まずは全体像から。
3つの共通化手段の対応マップ
何を共通化したいかで、向く手段が変わってきます。

ざっくりの判断は「塊なら部分ビュー、小物なら HTML Helper、ロジックが要るなら ViewComponent」。順に見ていきます。
手段1: 部分ビュー — HTML の塊を使い回す
一番よく使うのが部分ビュー(Partial)です。.cshtml を1枚作って、モデルを渡して埋め込む。
@* Views/Shared/_OrderRow.cshtml *@
@model OrderViewModel
<tr>
<td>@Model.OrderNo</td>
<td>@Model.CustomerName</td>
<td>@Model.Total.ToString("C")</td>
</tr>
使う側は <partial> タグヘルパーで、明細を1行ずつ渡します。
<table>
@foreach (var order in Model.Orders)
{
<partial name="_OrderRow" model="order" />
}
</table>
明細行やフォームみたいな「HTML の塊」を使い回すなら、こんな感じで部分ビューが一番素直。ここで押さえておきたいのは、部品側は自分でデータを取りに行けないということ。必要な値は、呼び出し側から渡してやります。
💡
.NET Frameworkの MVC 5 では、<partial>タグの代わりに@Html.Partial("_OrderRow", order)を使います(第2引数がモデル)。
手段2: HTML Helper — 小さな HTML を関数で作る
ステータスのバッジみたいに「小さくて、ちょっと C# のロジックが要る HTML」は、HTML Helper(拡張メソッド)が向きます。
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
public static class HtmlHelperExtensions
{
public static IHtmlContent StatusBadge(this IHtmlHelper html, int status)
{
var (text, css) = status switch
{
0 => ("未処理", "badge-secondary"),
1 => ("処理中", "badge-primary"),
2 => ("完了", "badge-success"),
_ => ("不明", "badge-dark"),
};
return new HtmlString($"<span class=\"badge {css}\">{text}</span>");
}
}
呼び出しは1行です。
@Html.StatusBadge(order.Status)
部分ビューを1枚作るほどでもない、でも毎回 if を書くのはだるい。そのくらいの小物にちょうどいい。ただ、HTML を文字列で組むやり方なので、中身が大きくなってきたら部分ビューに移すサインです。
💡 MVC 5 では、戻り値の
IHtmlContentがMvcHtmlString、this IHtmlHelperがthis HtmlHelperになります(return new MvcHtmlString(...))。
手段3: ViewComponent — 独自ロジックを持てる部品
部品の中で DB アクセスなどのロジックを持ちたいなら ViewComponent。3つの中でこれだけコントローラに近い立ち位置で、依存を注入して非同期で書けます。
using Microsoft.AspNetCore.Mvc;
public class CartSummaryViewComponent : ViewComponent
{
private readonly IOrderService _service;
public CartSummaryViewComponent(IOrderService service) => _service = service;
public async Task<IViewComponentResult> InvokeAsync(int userId)
{
// 部品の中で DB アクセスできる(部分ビューには無い強み)
var count = await _service.GetCartCountAsync(userId);
return View(count);
}
}
対応するビューを置いて(Views/Shared/Components/CartSummary/Default.cshtml)、呼び出しはこう書きます。
<vc:cart-summary user-id="123" />
カートの件数、サイドバーの通知、ログインユーザー情報。「どの画面でも出るけど、中でデータを取ってくる」部品には、ViewComponent がいい感じにハマります。
💡
.NET Frameworkの MVC 5 には ViewComponent がありません。 相当するのは「子アクション」で、[ChildActionOnly]を付けたアクションを@Html.Action("CartSummary")で呼びます。Core に上げるとこれが ViewComponent に置き換わる、と覚えておくと移行が楽です。
ミニマム検証: どれを選ぶか、フローで決める
迷ったら、この順に自問すると早いです。

まず分かれるのは「中でデータを取るか?」。取るなら ViewComponent 一択です。取らないなら、塊か小物かで部分ビューと HTML Helper を選ぶ。それだけ。
ハマりポイント: 知らないと最初に詰まるやつ
私が実際にやられた順に2つ挙げておきます。
① 部分ビューに、親のモデルが勝手に渡ってくる
<partial> に model を書かないと、呼び出し元ビューの Model がそのまま渡ります。
@* NG: model を省略。親の Model(一覧全体の型)が渡ってきて型不一致で落ちる *@
<partial name="_OrderRow" />
@* OK: 部品が期待する型を明示的に渡す *@
<partial name="_OrderRow" model="order" />
「部品なんだから空で呼べるでしょ??」と思って model を省くと、_OrderRow は OrderViewModel を期待してるのに一覧全体の型が飛んできて、実行時にドカンといきます。対策はシンプルで、部分ビューには渡す型を必ず明示する。これで防げます。
② ViewComponent を URL で叩こうとする
ViewComponent は見た目こそコントローラに似てますが、URL から直接アクセスはできません。ルーティングに乗らないんですよね。呼べるのはビューの中から(<vc:...> か @await Component.InvokeAsync(...))だけ。
「/CartSummary で開けるはず」とブラウザに打って 404 で悩む。これが最初のあるあるです。ViewComponent は「画面の部品」であって「画面そのもの」じゃない。そう割り切ると迷いません。
私の現場メモ: 作り分けの基準は「データを取るか」
キャッチアップして一番効いたのは、「部品の中でデータを取りに行くか?」を最初に問うこと。これでした。
取りに行くなら ViewComponent。取りに行かない(呼び出し側からもらうだけ)なら部分ビューか HTML Helper。この一本の問いで、9割は迷わなくなります!!
あと、最初から作り分けを完璧にしようとしなくていい。まずコピペしてる塊を部分ビューに切り出す。それだけで保守はだいぶ楽になります。ロジックが要るようになった部品だけ、あとから ViewComponent に格上げすればいい。こんな感じで、育てながら作り分ければ十分です。
まとめ
ASP.NET の共通部品、3手段の作り分けを畳むとこうです。
- 部分ビュー: HTML の塊を使い回す。データは呼び出し側から渡す(
modelの明示を忘れない) - HTML Helper: バッジみたいな小さな HTML を C# のロジック付きで生成
- ViewComponent: 部品の中で DB アクセスなど独自ロジックを持てる。非同期で書ける
- 判断は 「部品の中でデータを取りに行くか?」 の一問から
.NET FrameworkMVC 5 に ViewComponent は無い → 子アクション(@Html.Action+[ChildActionOnly])が相当
コピペしてた塊を1つ切り出すだけでも、画面の保守はぐっと楽になります。まずは一番よくコピペしてる明細行から、部分ビューにしてみてください。
動作確認メモ: HTML Helper と ViewComponent の C# は ASP.NET Core 参照込みでコンパイル確認済みです(Razor ビューの描画は実 ASP.NET プロジェクトで別途お試しください)。なお
@Html.StatusBadgeや<vc:cart-summary>をビューで使うには、_ViewImports.cshtmlに拡張メソッドの名前空間の@usingと@addTagHelper *, プロジェクト名を1回入れておく必要があります。
よくある質問
Q1. 部分ビューと ViewComponent はどう使い分けますか?
モデルを受け取って HTML の塊を使い回すだけなら部分ビュー、部品の中で DB アクセスなど独自のロジックが必要なら ViewComponent です。部分ビューはビューから直接呼べますが、自分でデータを取りには行けません。
Q2. 部分ビューに親のモデルが勝手に渡ってくるのはなぜですか?
ASP.NET Core では <partial> に model を明示しないと、呼び出し元ビューの Model がそのまま渡されます。部分ビューが別の型を期待していると型不一致で落ちるので、model 属性で明示的に渡すのが安全です。
Q3. ViewComponent は .NET Framework の MVC 5 でも使えますか?
使えません。ViewComponent は ASP.NET Core からの機能です。.NET Framework の MVC 5 では、[ChildActionOnly] を付けた子アクションを @Html.Action で呼ぶのが相当します。
Q4. HTML Helper はいつ使うのがいいですか?
バッジやアイコンのような小さな HTML を、C# のロジック付きで生成したい時に向きます。大きな塊なら部分ビュー、DB アクセスが要るなら ViewComponent の方が適しています。
次に読むべき記事





以上!
同じところで画面をコピペしてた人がいたら、どんどんシェア待ってるぜ!!
執筆者
バイブス父さん — 業務 SE 7 年(正社員 2 / フリーランス 5)。現職は SEO 直轄部の AI アドバイザー兼 PL、副業で中小 SIer の CTO。SES 複数社・フリーランスエージェント複数経由の経験ベースで「業務 SE 視点」の技術 + キャリア記事を書いています。
🐦 X: @hiro_progra0524(日々の現場メモ更新中)
📝 About Me で経歴詳細を見る


