You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The admin content list hardcodes its columns: Title / Status / (Locale when i18n is on) / Date (packages/admin/src/components/ContentList.tsx). For collections whose identity lives in a custom field, the list is a wall of near-identical rows and editors open every entry to see the one field they're scanning for.
Three concrete cases from a production migration (a film-festival site moving off WordPress):
Sponsors — editors retag tier (select), festival_track (select) and sort_order (integer) across ~80 partners. The list shows none of them.
Contact inbox — a contact_submissions collection where triage lives in a submission_status select. We currently prefix the status into the title via a beforeSave hook ("[NUEVO] name · consulta") purely so it's visible in the list — a workaround that burns the title field for what should be a column.
Screenings — start_datetime + a venue reference are the two things an editor scans for; neither is visible.
We've built an interim read-only plugin admin page that re-lists one collection with its fields as columns, but that's a parallel surface that duplicates the stock list and has to be retired later. The gap is general and feels like it belongs in core.
Proposal
Let a collection declare extra list columns from custom fields.
Option A — collection-level config (my preference):
Storage: a nullable JSON column on _emdash_collections (migration + SchemaRegistry + seed apply), or a scalar column if you'd rather avoid JSON there.
Manifest: _buildManifest already emits per-field kind / label / select options; the collection entry gains listColumns: string[]. Unknown/removed field slugs are dropped at manifest build (renames can't strand the admin).
Admin: ContentList inserts one <td> per declared column between Title and Status. Rows already carry full data (getItemTitle reads item.data.title), so no API/server change is needed for rendering.
Option B — field-level flag ("showInList": true on the field def): less plumbing conceptually, but column order becomes implicit (field order) and the collection-level intent ("this list shows these three things") gets scattered across field defs. Mentioning it in case you prefer the schema to stay field-centric.
Rendering rules (v1)
select / multiSelect: resolve value → option label from the manifest options; unknown values render raw (visible, not blank).
string / number / integer: as-is; boolean: ✓ / —; datetime: the same relative/short format the Date column uses.
reference / repeater / portableText / image / file: excluded in v1 (references need a join or N+1 hydration — out of scope until there's a story for it; happy to note it as follow-up).
Empty/missing values render an em dash.
Cap the declared columns (e.g. 4) so the table stays usable; overflow is ignored with a console warning at manifest build.
No filtering, no per-user column preferences — this is a schema-declared, collection-wide setting.
What I'd ship in the PR
Migration + SchemaRegistry read/write + seed-schema support (+ emdash types passthrough if applicable)
Manifest emission with unknown-slug pruning
ContentList rendering (Lingui-wrapped headers from field labels), matching the existing table/i18n patterns
Tests: core (registry round-trip, manifest pruning, seed apply) and admin (column rendering incl. select-label resolution, cap, empty-state) on real in-memory SQLite per the house rules; changeset (emdash minor + @emdash-cms/admin minor)
Happy to adjust the shape (A vs B, naming, cap, which kinds render in v1) to whatever fits the project's direction — nothing here is precious. If approved I'll open the PR with the AI-disclosure box checked (built with Claude Code; reviewed and owned by me).
Prior related threads I checked: #1533 (custom-field index full-scans), #1649 (indexed fields), #1523 (bulk actions in the content list — adjacent but orthogonal). I didn't find an existing proposal for list columns.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
Problem
The admin content list hardcodes its columns: Title / Status / (Locale when i18n is on) / Date (
packages/admin/src/components/ContentList.tsx). For collections whose identity lives in a custom field, the list is a wall of near-identical rows and editors open every entry to see the one field they're scanning for.Three concrete cases from a production migration (a film-festival site moving off WordPress):
tier(select),festival_track(select) andsort_order(integer) across ~80 partners. The list shows none of them.contact_submissionscollection where triage lives in asubmission_statusselect. We currently prefix the status into the title via abeforeSavehook ("[NUEVO] name · consulta") purely so it's visible in the list — a workaround that burns the title field for what should be a column.start_datetime+ avenuereference are the two things an editor scans for; neither is visible.We've built an interim read-only plugin admin page that re-lists one collection with its fields as columns, but that's a parallel surface that duplicates the stock list and has to be retired later. The gap is general and feels like it belongs in core.
Proposal
Let a collection declare extra list columns from custom fields.
Option A — collection-level config (my preference):
_emdash_collections(migration +SchemaRegistry+ seed apply), or a scalar column if you'd rather avoid JSON there._buildManifestalready emits per-fieldkind/label/ selectoptions; the collection entry gainslistColumns: string[]. Unknown/removed field slugs are dropped at manifest build (renames can't strand the admin).ContentListinserts one<td>per declared column between Title and Status. Rows already carry fulldata(getItemTitlereadsitem.data.title), so no API/server change is needed for rendering.Option B — field-level flag (
"showInList": trueon the field def): less plumbing conceptually, but column order becomes implicit (field order) and the collection-level intent ("this list shows these three things") gets scattered across field defs. Mentioning it in case you prefer the schema to stay field-centric.Rendering rules (v1)
select/multiSelect: resolve value → option label from the manifest options; unknown values render raw (visible, not blank).string/number/integer: as-is;boolean: ✓ / —;datetime: the same relative/short format the Date column uses.reference/repeater/portableText/image/file: excluded in v1 (references need a join or N+1 hydration — out of scope until there's a story for it; happy to note it as follow-up).Deliberate non-goals (v1)
orderBywhitelist staystitle|status|locale|updatedAt. Custom-field sort without indexes full-scans (exactly the issue Collection tables index only system columns — custom editorial fields used in where/orderBy get no index, so live list queries full-scan on D1 #1533 raises for D1); if/whenindexedfields land (discussion Add `indexed` option for custom field B-tree indexes #1649), the whitelist could grow per-column as a follow-up.What I'd ship in the PR
SchemaRegistryread/write + seed-schema support (+emdash typespassthrough if applicable)ContentListrendering (Lingui-wrapped headers from field labels), matching the existing table/i18n patternsemdashminor +@emdash-cms/adminminor)Happy to adjust the shape (A vs B, naming, cap, which kinds render in v1) to whatever fits the project's direction — nothing here is precious. If approved I'll open the PR with the AI-disclosure box checked (built with Claude Code; reviewed and owned by me).
Prior related threads I checked: #1533 (custom-field index full-scans), #1649 (
indexedfields), #1523 (bulk actions in the content list — adjacent but orthogonal). I didn't find an existing proposal for list columns.Beta Was this translation helpful? Give feedback.
All reactions