動くコード図鑑技術記事現場の渡り方キャリア論すべての記事About
技術記事

ASP.NET MVC で画面の共通部品を作り分ける(部分ビュー / HTML Helper / ViewComponent)

バイブス父さん
現役の業務SE
2026年9月19日13 min read広告 (PR) を含む場合があります
ASP.NET MVC で画面の共通部品を作り分ける(部分ビュー / HTML Helper / ViewComponent)

ASP.NET MVC で画面の共通部品を作り分ける(部分ビュー / HTML Helper / ViewComponent)

みなさんこんにちは!ヒロポンです!

一覧画面の明細行、検索フォーム、ステータスのバッジ。同じ HTML を画面ごとにコピペして、あとから1箇所直すのに全画面を触るハメになったこと、ありませんか??

ASP.NET MVC には、この「共通部品」を作る手段が3つあります。部分ビューHTML HelperViewComponent

この記事は、ASP.NET 部分ビュー 使い分けで迷う人向けに、3つを「何を共通化するのに向くか」で作り分けて整理していきます。全部モデルを渡すだけの部品にすればいい、という話じゃないんですよね。

💡 コードは基本 ASP.NET Core MVC 前提です。.NET Framework の MVC 5 の場合、ViewComponent の代わりに子アクション(後述)を使います。

明細行を毎回コピペしていた話

私も最初は、一覧画面の <tr> を画面ごとにコピペしてました。3画面くらいまでなら、まあ平気なんです。

きついのは、列を1つ足す時。全画面を開いて、同じ <td> を足して回る。1箇所ズレて、あとで「あの画面だけ表示が違う」で呼ばれる。地味に消耗します。

共通部品にすれば、直すのは1ファイル。ただ、どの手段で共通化するかを間違えると、逆に読みにくくなるんですよね。まずは全体像から。

3つの共通化手段の対応マップ

何を共通化したいかで、向く手段が変わってきます。

部分ビュー・HTML Helper・ViewComponent を、主用途・独自ロジック・モデル受け渡し・非同期・書く場所・本命用途で比較した対応マップ

ざっくりの判断は「塊なら部分ビュー、小物なら 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 では、戻り値の IHtmlContentMvcHtmlStringthis IHtmlHelperthis 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 に置き換わる、と覚えておくと移行が楽です。

ミニマム検証: どれを選ぶか、フローで決める

迷ったら、この順に自問すると早いです。

共通化したい部品について、独自ロジックやDBアクセスが要るかで ViewComponent、HTMLの塊なら部分ビュー、小さな装飾なら HTML Helper に振り分ける判断フロー

まず分かれるのは「中でデータを取るか?」。取るなら ViewComponent 一択です。取らないなら、塊か小物かで部分ビューと HTML Helper を選ぶ。それだけ。

ハマりポイント: 知らないと最初に詰まるやつ

私が実際にやられた順に2つ挙げておきます。

① 部分ビューに、親のモデルが勝手に渡ってくる

<partial>model を書かないと、呼び出し元ビューの Model がそのまま渡ります

@* NG: model を省略。親の Model(一覧全体の型)が渡ってきて型不一致で落ちる *@
<partial name="_OrderRow" />

@* OK: 部品が期待する型を明示的に渡す *@
<partial name="_OrderRow" model="order" />

「部品なんだから空で呼べるでしょ??」と思って model を省くと、_OrderRowOrderViewModel を期待してるのに一覧全体の型が飛んできて、実行時にドカンといきます。対策はシンプルで、部分ビューには渡す型を必ず明示する。これで防げます。

② 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 Framework MVC 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 で経歴詳細を見る


この記事のコードと手順は ぜんぶ動作検証済み。 安心して現場で試してくれ。
バイブス父さん

現役の業務SE。C# / SQL Server 保守の現場から、コードも人もキャリアも全部書く。 実体験ベース。

運営者について