Skip to content

Repository files navigation

UmbHost.Tables

A table property editor for Umbraco 17 and 18 that allows content editors to create and manage tabular data with support for header rows, header columns, and inline editing.

Features

  • Inline cell editing with contenteditable
  • Header row and header column toggles
  • Add and remove rows and columns
  • Right-click context menu for quick operations
  • Strongly-typed C# models with PropertyValueConverter
  • Configurable min/max rows and columns
  • Read-only mode support
  • Built with Lit/Vite/TypeScript following Umbraco 17 patterns

Version compatibility

The package major tracks the Umbraco major, so pick the line that matches your site:

Umbraco UmbHost.Tables Branch
18.x 18.x main
17.x 17.x v17/main

Both lines are built from the same source; the 17 line receives fixes by cherry-pick.

Installation

Install the NuGet package:

dotnet add package UmbHost.Tables

Or via the Package Manager Console:

Install-Package UmbHost.Tables

NuGet resolves the newest version compatible with your Umbraco install. To pin a line explicitly:

dotnet add package UmbHost.Tables --version 17.*

Usage

Creating a Data Type

  1. In the Umbraco backoffice, go to SettingsData Types
  2. Click Create Data Type
  3. Select Table as the property editor
  4. Configure your options (header toggles, row/column limits, etc.)
  5. Save the Data Type

Adding to a Document Type

  1. Edit your Document Type
  2. Add a new property
  3. Select your Table Data Type
  4. Save the Document Type

Rendering in Razor Views

@using UmbHost.Tables.Models
@{
    var table = Model.Value<TableModel>("tableProperty");
}

@if (table != null && table.Rows.Any())
{
    <table class="table">
        @if (table.UseFirstRowAsHeader && table.Rows.Any())
        {
            <thead>
                <tr>
                    @foreach (var cell in table.Rows.First().Cells)
                    {
                        <th>@Html.Raw(cell.Value)</th>
                    }
                </tr>
            </thead>
        }
        <tbody>
            @foreach (var row in table.UseFirstRowAsHeader ? table.Rows.Skip(1) : table.Rows)
            {
                <tr>
                    @for (var i = 0; i < row.Cells.Count; i++)
                    {
                        var cell = row.Cells[i];
                        if (table.UseFirstColumnAsHeader && i == 0)
                        {
                            <th>@Html.Raw(cell.Value)</th>
                        }
                        else
                        {
                            <td>@Html.Raw(cell.Value)</td>
                        }
                    }
                </tr>
            }
        </tbody>
    </table>
}

Setup

Add these to Views/_ViewImports.cshtml once:

@using UmbHost.Tables.Models
@using UmbHost.Tables.Extensions
@using UmbHost.Tables.Rendering
@addTagHelper *, UmbHost.Tables

ToHtmlTable needs the first two usings, TableHtmlOptions needs the third, and the tag helper needs the addTagHelper line.

Using the Tag Helper

@{
    var table = Model.Value<TableModel>("tableProperty");
}

<umbhost-table table="@table" class="table table-striped" />

class, id, data-* and any other attribute you write are passed straight through to the rendered <table>. Inner elements have their own class hooks:

<umbhost-table table="@table"
               class="table table-striped"
               id="prices"
               data-sortable="true"
               head-class="thead-dark"
               body-class="table-group-divider"
               row-class="align-middle"
               header-cell-class="fw-bold"
               cell-class="px-4 py-2" />

Nothing is rendered when the table is null or empty, so no @if guard is needed.

Using the Extension Method

@{
    var table = Model.Value<TableModel>("tableProperty");
}

@table.ToHtmlTable("table table-striped")

For full control, pass TableHtmlOptions instead of a class string:

@table.ToHtmlTable(new TableHtmlOptions
{
    Class = "table table-striped",
    Id = "prices",
    CellClass = "px-4 py-2",
    Attributes = new Dictionary<string, string?> { ["data-sortable"] = "true" },
})

ToHtmlTable returns IHtmlContent, so @Html.Raw(table.ToHtmlTable("table")) also works.

Generated Markup

Both helpers produce the same markup. Header cells are determined by cell.Type or the UseFirstRowAsHeader / UseFirstColumnAsHeader flags, and carry scope for screen readers:

<table class="table table-striped">
  <thead>
    <tr>
      <th scope="col">Plan</th>
      <th scope="col">Price</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Starter</th>
      <td><a href="/pricing/">£10</a></td>
    </tr>
  </tbody>
</table>

Models

TableModel

The main model representing the table:

Property Type Description
Rows IReadOnlyList<TableRow> Collection of table rows
UseFirstRowAsHeader bool Whether the first row should render as <th> elements
UseFirstColumnAsHeader bool Whether the first column should render as <th> elements
RowCount int Number of rows
ColumnCount int Number of columns (from first row)
HasContent bool Whether the table has at least one row containing a non-empty cell
IsEmpty bool Inverse of HasContent
Cells IReadOnlyList<IReadOnlyList<TableCell>> The rows projected to a two-dimensional list of cells
HeaderRow TableRow? The first row when UseFirstRowAsHeader is true, otherwise null
BodyRows IReadOnlyList<TableRow> The rows excluding the header row when UseFirstRowAsHeader is true, otherwise every row
HeaderColumn IReadOnlyList<TableCell> The first cell of every row when UseFirstColumnAsHeader is true, otherwise empty

Methods:

Method Returns Description
GetCell(int rowIndex, int columnIndex) TableCell? The cell at the given position, or null if either index is out of bounds
GetRow(int index) TableRow? The row at the given index, or null if out of bounds
GetColumn(int columnIndex) IReadOnlyList<TableCell> Every cell in the column, skipping rows that have too few cells

TableRow

Represents a single row:

Property Type Description
Cells IReadOnlyList<TableCell> Collection of cells in the row
IsHeaderRow bool Whether every cell in the row is a header
IsEmpty bool Whether all cells are empty
CellCount int Number of cells

TableCell

Represents a single cell:

Property Type Description
Value string HTML/text content
Type TableCellType Td or Th
ColSpan int Column span. Reserved; cell spanning is not implemented, so this is always 1
RowSpan int Row span. Reserved; cell spanning is not implemented, so this is always 1
IsEmpty bool Whether cell is empty
IsHeader bool Whether cell is a header
IsSpanned bool Whether the cell spans multiple rows or columns. Always false while spanning is unimplemented

Configuration Options

When creating a Data Type, the following options are available:

Option Default Description
showUseFirstRowAsHeader true Show the "use first row as header" toggle
showUseFirstColumnAsHeader true Show the "use first column as header" toggle
defaultRows 3 Initial number of rows for new tables
defaultColumns 3 Initial number of columns for new tables
minRows 1 Minimum allowed rows
maxRows 0 Maximum allowed rows (0 = unlimited)
minColumns 1 Minimum allowed columns
maxColumns 0 Maximum allowed columns (0 = unlimited)

Requirements

  • Umbraco 17.0.0 or later
  • .NET 10.0 or later

Development

Prerequisites

  • .NET 10 SDK
  • Node.js 18+

Building from Source

Clone the repository and build the client assets:

cd UmbHost.Tables.Client/
npm install
npm run build

Build the .NET project:

dotnet build

Watching for Changes

During development, you can watch for client-side changes:

cd UmbHost.Tables.Client/
npm run watch

Creating a NuGet Package

dotnet pack UmbHost.Tables/UmbHost.Tables.csproj -c Release

Migrating from Limbo.Umbraco.Tables

If you're migrating from Limbo.Umbraco.Tables:

  1. The data structure is compatible, so existing content should work without migration
  2. Update your using statements from Limbo.Umbraco.Tables.Models to UmbHost.Tables.Models
  3. The TableModel properties are largely the same
  4. Update your Data Types to use the new "Table" property editor

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Credits

Inspired by Limbo.Umbraco.Tables by Limbo.

Built for Umbraco 17+ by UmbHost.

Support

About

A table property editor for Umbraco 17+ that allows content editors to create and manage tabular data with support for header rows, header columns, and inline editing.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages