DataTable

DataTable is the basic tabular-data widget in the authoring set.
Use it when users need to scan rows against shared columns: files, users, invoices, runs, and similar structured records. A table solves a different problem than a list or card grid. It trades visual warmth for fast comparison across fields.

Example

use fission::prelude::*;
use std::sync::Arc;

let widget: Widget = DataTable {
    id: WidgetId::explicit("build_runs_table"),
    columns: vec![
        TableColumn {
            id: "status".into(),
            title: "Status".into(),
            width: 120.0,
            sortable: false,
        },
        TableColumn {
            id: "branch".into(),
            title: "Branch".into(),
            width: 180.0,
            sortable: true,
        },
    ],
    rows: vec![
        TableRow {
            id: "run_42".into(),
            cells: vec!["Passed".into(), "main".into()],
        },
    ],
    selected_ids: view.state().selected_run_ids.clone(),
    on_selection_change: Some(Arc::new(move |row_id| ActionEnvelope {
        id: toggle_row_selection_id,
        payload: serde_json::to_vec(&ToggleRowSelection(row_id)).unwrap(),
    })),
    on_select_all: Some(select_all_action),
    label: Some("Build runs".into()),
}
.into();
Here the table is responsible for presentation. The reducer is responsible for which rows are selected and what selection means.

Field table

Field
Type
Meaning
Notes / default behavior
id
WidgetId
Stable identity for the table instance.
Present in the public struct, but the checked-in renderer does not currently consume it internally.
columns
Vec<TableColumn>
Column definitions in display order.
Required. Width comes from the column records.
rows
Vec<TableRow>
Row data in display order.
Required. Each row's cells are matched to columns by index.
selected_ids
Vec<String>
Currently selected row ids.
Controlled by app state.
on_selection_change
Option<Arc<dyn Fn(String) -> ActionEnvelope + Send + Sync>>
Closure that builds the action for row or checkbox selection.
Called with the row id.
on_select_all
Option<ActionEnvelope>
Action dispatched by the header checkbox.
The checkbox is disabled when no action is supplied.
label
Option<String>
Accessible name for the table.
Recommended when surrounding context does not already name the table.

How to think about tabular data here

A table works when every row shares the same shape and when columns matter more than custom per-row layout. The checked-in DataTable is intentionally simple: cells are plain strings, not arbitrary widgets. That keeps comparison easy, but it also means complex media cells, nested controls, or heavily formatted content usually want a custom table layout instead.
Selection is explicit. The widget highlights rows whose ids appear in selected_ids, and it uses the same selection callback for row presses and row checkboxes.

Current behavior and limits

The application owns sorting and bulk-selection state. Supply TableColumn::on_sort for sortable headers and DataTable::on_select_all for the header checkbox; an unavailable action renders its affordance disabled. The header surface fills the table width even when the declared columns use less space, while header and body cells retain the same explicit column widths.

Specific advice

Keep column counts modest on narrow screens. If comparison is still important on mobile, consider a smaller table with fewer columns plus a drill-in detail view instead of forcing a huge horizontal matrix.

Production checklist

For DataTable, review the fields that change behavior before treating the widget as finished: id, columns, rows, selected_ids, on_selection_change. The goal is to make the product rule visible in state and actions, not hidden inside ad-hoc construction code.
•
Bind on_selection_change to explicit reducer actions and test that the reducer handles unavailable, duplicate, or invalid input safely.
•
Set id only when identity must be stable across filtering, reordering, diagnostics, or tests; otherwise let Fission derive identity from structure.
•
Check the semantics tree for the user-facing label or role that makes this widget understandable without relying only on pixels.
•
Add at least one component or harness test that confirms the visible text, semantic role, action dispatch, and layout constraint that matter for this widget in context.
If a screen starts repeating the same DataTable setup, extract a named component around this widget. That keeps the reference API small while making product code easier to read and safer for generated code to copy.
Previous
Code