Adds per-user pinning and flagging columns to Views table displays via htmx, with a pluggable row-action plugin type.

What it does

Adds an extra column to any Views table display that lets each logged-in user pin rows (so pinned rows float to the top on subsequent visits) and flag rows (a private bookmark for finding them later). The toggle uses htmx for instant, no-reload swaps, and per-user state persists across sessions.

The actions are pluggable: this module ships with Pin and Flag, but any contrib or custom module can register more (Bookmark, Follow, Hide, …) by exposing a single ViewsRowAction plugin class — they show up in the per-display settings checkbox list automatically.

Not related to the Flag contrib module — this module ships its own lightweight per-user storage (a single views_table_pinflag_state table) and has no dependency on Flag. The two can coexist.

Why this approach

It's a custom Views style plugin that extends core's Table — drop-in compatible with every existing table display. Switch the format from Table to Table (with pin / flag), save, and the column appears. Sortable columns, sticky header, grouping, captions — all native Views features keep working.

Features

  • Per-display configuration: choose enabled actions, column position (left or right), custom CSS class, sort-pinned-first toggle, and an optional column header label.
  • Site-wide defaults (optional) at /admin/config/user-interface/views-table-pinflag so multiple displays can inherit a single setting.
  • htmx-powered toggles with hx-post, CSRF-protected route, and entity view-access check on every request.
  • Cache strategy uses the per-(user, action) cache tag pattern: toggling invalidates only that user's view, never the entity's own render cache.
  • Anonymous users see no column (nothing to persist against).
  • State is scoped per (view, display) — pinning a node in one view does not pin it in another, so each list stays independently curated.
  • Idempotent storage in a single views_table_pinflag_state table keyed on (uid, action_id, view_id, display_id, entity_type, entity_id).
  • Pluggable plugin type — third-party row actions are discovered automatically and appear in the settings UI.

Requirements

  • Drupal ^10.3 || ^11
  • htmx module (provides the htmx library)
  • An entity-based view (rows must expose _entity or have a base table mapping to an entity type — covers nodes, users, taxonomy terms, custom entities, etc.)

Installation

composer require drupal/views_table_pinflag
drush pm:install views_table_pinflag
drush cr

Configuration

  1. Edit any view with a table display.
  2. Click Format → Table and switch to Table (with pin / flag).
  3. Pick which actions to enable, the column position, and any custom class for theming.
  4. Save the view.

Permissions

  • Use Views table pin/flag actions — required to see and toggle the column.
  • Administer Views Table Pin/Flag — access the global defaults form.

Extending

Define a class in your_module/src/Plugin/ViewsRowAction/Bookmark.php:

#[ViewsRowAction(
  id: 'bookmark',
  label: new TranslatableMarkup('Bookmark'),
  label_activate: new TranslatableMarkup('Bookmark row'),
  label_deactivate: new TranslatableMarkup('Remove bookmark'),
  icon: '<svg …>…</svg>',
  weight: 5,
)]
class Bookmark extends RowActionBase {}

After a cache rebuild, Bookmark appears in every Pinflag table's enabled-actions checkboxes.

Limitations & Roadmap

  • "Sort pinned first" is current-page only; cross-page DB-level sort needs a Views query alter (planned).
  • Rows without a resolvable entity render an empty cell (column position and custom class still apply, so the table layout doesn't shift).
  • Planned: an exposed "show only mine" filter, Views Bulk Operations integration, cross-page DB sort via query alter.

Project information

Releases