Table
A data table built from composable parts.
<table.Table>
<table.TableCaption>A list of invoices.</table.TableCaption>
<table.TableHeader>
<table.TableRow>
<table.TableHead>Invoice</table.TableHead>
<table.TableHead>Status</table.TableHead>
<table.TableHead>Amount</table.TableHead>
</table.TableRow>
</table.TableHeader>
<table.TableBody>
<table.TableRow>
<table.TableCell>INV-001</table.TableCell>
<table.TableCell>Paid</table.TableCell>
<table.TableCell>$120.00</table.TableCell>
</table.TableRow>
<table.TableRow>
<table.TableCell>INV-002</table.TableCell>
<table.TableCell>Open</table.TableCell>
<table.TableCell>$80.00</table.TableCell>
</table.TableRow>
</table.TableBody>
</table.Table>Installation
Run the command in the app module.
gx add tableThe command writes these files.
| File | Path in the app |
|---|---|
Table.gx |
ui/table/Table.gx |
TableHeader.gx |
ui/table/TableHeader.gx |
TableBody.gx |
ui/table/TableBody.gx |
TableFooter.gx |
ui/table/TableFooter.gx |
TableRow.gx |
ui/table/TableRow.gx |
TableHead.gx |
ui/table/TableHead.gx |
TableCell.gx |
ui/table/TableCell.gx |
TableCaption.gx |
ui/table/TableCaption.gx |
Table.fixtures.go |
ui/table/Table.fixtures.go |
TableBody.fixtures.go |
ui/table/TableBody.fixtures.go |
TableCaption.fixtures.go |
ui/table/TableCaption.fixtures.go |
TableCell.fixtures.go |
ui/table/TableCell.fixtures.go |
TableFooter.fixtures.go |
ui/table/TableFooter.fixtures.go |
TableHead.fixtures.go |
ui/table/TableHead.fixtures.go |
TableHeader.fixtures.go |
ui/table/TableHeader.fixtures.go |
TableRow.fixtures.go |
ui/table/TableRow.fixtures.go |
Copy each file to its path in the app. Change each import of a registry package to the path of that package in the app.
package table
props {
// Class adds classes to the table element.
Class string = ""
// Children is the content of the table: the caption, header, body and footer.
Children gx.Node
// Attrs adds HTML attributes to the table element.
Attrs gx.Attrs = nil
}
<div data-slot="table-container" class="relative w-full overflow-x-auto">
<table data-slot="table" class={gx.Cx("w-full caption-bottom text-sm", p.Class)} {...p.Attrs}>{p.Children}</table>
</div>package table
props {
// Class adds classes to the root element.
Class string = ""
// Children is the rows of the header.
Children gx.Node
// Attrs adds HTML attributes to the root element.
Attrs gx.Attrs = nil
}
<thead data-slot="table-header" class={gx.Cx("[&_tr]:border-b", p.Class)} {...p.Attrs}>{p.Children}</thead>package table
props {
// Class adds classes to the root element.
Class string = ""
// Children is the rows of the body.
Children gx.Node
// Attrs adds HTML attributes to the root element.
Attrs gx.Attrs = nil
}
<tbody data-slot="table-body" class={gx.Cx("[&_tr:last-child]:border-0", p.Class)} {...p.Attrs}>{p.Children}</tbody>package table
props {
// Class adds classes to the root element.
Class string = ""
// Children is the rows of the footer.
Children gx.Node
// Attrs adds HTML attributes to the root element.
Attrs gx.Attrs = nil
}
<tfoot data-slot="table-footer" class={gx.Cx("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", p.Class)} {...p.Attrs}>{p.Children}</tfoot>package table
props {
// Class adds classes to the root element.
Class string = ""
// Children is the cells of the row.
Children gx.Node
// Attrs adds HTML attributes to the root element.
Attrs gx.Attrs = nil
}
<tr data-slot="table-row" class={gx.Cx("border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted", p.Class)} {...p.Attrs}>{p.Children}</tr>package table
props {
// Class adds classes to the root element.
Class string = ""
// Children is the content of the header cell.
Children gx.Node
// Attrs adds HTML attributes to the root element.
Attrs gx.Attrs = nil
}
<th data-slot="table-head" class={gx.Cx("h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has(:is([role=checkbox],input[type=checkbox],label:has(>input[type=checkbox])))]:pr-0 [&>:is([role=checkbox],input[type=checkbox],label:has(>input[type=checkbox]))]:translate-y-[2px]", p.Class)} {...p.Attrs}>{p.Children}</th>package table
props {
// Class adds classes to the root element.
Class string = ""
// Children is the content of the cell.
Children gx.Node
// Attrs adds HTML attributes to the root element.
Attrs gx.Attrs = nil
}
<td data-slot="table-cell" class={gx.Cx("p-2 align-middle whitespace-nowrap [&:has(:is([role=checkbox],input[type=checkbox],label:has(>input[type=checkbox])))]:pr-0 [&>:is([role=checkbox],input[type=checkbox],label:has(>input[type=checkbox]))]:translate-y-[2px]", p.Class)} {...p.Attrs}>{p.Children}</td>package table
props {
// Class adds classes to the root element.
Class string = ""
// Children is the caption text.
Children gx.Node
// Attrs adds HTML attributes to the root element.
Attrs gx.Attrs = nil
}
<caption data-slot="table-caption" class={gx.Cx("mt-4 text-sm text-muted-foreground", p.Class)} {...p.Attrs}>{p.Children}</caption>package table
import "github.com/alternayte/gx"
// invoiceRows are the body rows of the table fixtures.
func invoiceRows() gx.Node {
return gx.Frag(
TableRow(TableRowProps{Children: gx.Frag(
TableCell(TableCellProps{Children: gx.Text("INV-001")}),
TableCell(TableCellProps{Children: gx.Text("Paid")}),
TableCell(TableCellProps{Children: gx.Text("$120.00")}),
)}),
TableRow(TableRowProps{Children: gx.Frag(
TableCell(TableCellProps{Children: gx.Text("INV-002")}),
TableCell(TableCellProps{Children: gx.Text("Open")}),
TableCell(TableCellProps{Children: gx.Text("$80.00")}),
)}),
)
}
// invoiceHead is the header row of the table fixtures.
func invoiceHead() gx.Node {
return TableHeader(TableHeaderProps{Children: TableRow(TableRowProps{Children: gx.Frag(
TableHead(TableHeadProps{Children: gx.Text("Invoice")}),
TableHead(TableHeadProps{Children: gx.Text("Status")}),
TableHead(TableHeadProps{Children: gx.Text("Amount")}),
)})})
}
var TableFixtures = gx.Fixtures[TableProps]{
"Simple": {Children: gx.Frag(
TableCaption(TableCaptionProps{Children: gx.Text("A list of invoices.")}),
invoiceHead(),
TableBody(TableBodyProps{Children: invoiceRows()}),
)},
"Footer": {Children: gx.Frag(
invoiceHead(),
TableBody(TableBodyProps{Children: invoiceRows()}),
TableFooter(TableFooterProps{Children: TableRow(TableRowProps{Children: gx.Frag(
TableCell(TableCellProps{Attrs: gx.Attrs{{Key: "colspan", Value: "2"}}, Children: gx.Text("Total")}),
TableCell(TableCellProps{Children: gx.Text("$200.00")}),
)})}),
)},
"Selected": {Children: gx.Frag(
invoiceHead(),
TableBody(TableBodyProps{Children: gx.Frag(
TableRow(TableRowProps{Attrs: gx.Attrs{{Key: "data-state", Value: "selected"}}, Children: gx.Frag(
TableCell(TableCellProps{Children: gx.Text("INV-001")}),
TableCell(TableCellProps{Children: gx.Text("Paid")}),
TableCell(TableCellProps{Children: gx.Text("$120.00")}),
)}),
TableRow(TableRowProps{Children: gx.Frag(
TableCell(TableCellProps{Children: gx.Text("INV-002")}),
TableCell(TableCellProps{Children: gx.Text("Open")}),
TableCell(TableCellProps{Children: gx.Text("$80.00")}),
)}),
)}),
)},
}package table
import "github.com/alternayte/gx"
var TableBodyFixtures = gx.Fixtures[TableBodyProps]{"Empty": {}}package table
import "github.com/alternayte/gx"
var TableCaptionFixtures = gx.Fixtures[TableCaptionProps]{"Caption": {Children: gx.Text("A caption.")}}package table
import "github.com/alternayte/gx"
var TableCellFixtures = gx.Fixtures[TableCellProps]{"Cell": {Children: gx.Text("Value")}}package table
import "github.com/alternayte/gx"
var TableFooterFixtures = gx.Fixtures[TableFooterProps]{"Empty": {}}package table
import "github.com/alternayte/gx"
var TableHeadFixtures = gx.Fixtures[TableHeadProps]{"Header": {Children: gx.Text("Column")}}package table
import "github.com/alternayte/gx"
var TableHeaderFixtures = gx.Fixtures[TableHeaderProps]{"Empty": {}}package table
import "github.com/alternayte/gx"
var TableRowFixtures = gx.Fixtures[TableRowProps]{"Empty": {}}The theme must define these tokens: --border, --muted, --muted-foreground, --foreground.
Usage
<table.Table>
<table.TableHeader>
<table.TableRow>
<table.TableHead>Invoice</table.TableHead>
<table.TableHead>Status</table.TableHead>
</table.TableRow>
</table.TableHeader>
<table.TableBody>
<table.TableRow>
<table.TableCell>INV-001</table.TableCell>
<table.TableCell>Paid</table.TableCell>
</table.TableRow>
</table.TableBody>
</table.Table>For sorting, filtering and paging over many rows, use the data table component.
TableFooter holds the total rows. Set data-state="selected" on a TableRow to mark it.
A row that holds an expanded control takes the hover colour.
Pass class="text-right" to a TableHead and its cells for a number column.
Examples
Table: Simple
<table.Table>
<table.TableCaption>A list of invoices.</table.TableCaption>
<table.TableHeader>
<table.TableRow>
<table.TableHead>Invoice</table.TableHead>
<table.TableHead>Status</table.TableHead>
<table.TableHead>Amount</table.TableHead>
</table.TableRow>
</table.TableHeader>
<table.TableBody>
<table.TableRow>
<table.TableCell>INV-001</table.TableCell>
<table.TableCell>Paid</table.TableCell>
<table.TableCell>$120.00</table.TableCell>
</table.TableRow>
<table.TableRow>
<table.TableCell>INV-002</table.TableCell>
<table.TableCell>Open</table.TableCell>
<table.TableCell>$80.00</table.TableCell>
</table.TableRow>
</table.TableBody>
</table.Table>Table: Footer
<table.Table>
<table.TableHeader>
<table.TableRow>
<table.TableHead>Invoice</table.TableHead>
<table.TableHead>Status</table.TableHead>
<table.TableHead>Amount</table.TableHead>
</table.TableRow>
</table.TableHeader>
<table.TableBody>
<table.TableRow>
<table.TableCell>INV-001</table.TableCell>
<table.TableCell>Paid</table.TableCell>
<table.TableCell>$120.00</table.TableCell>
</table.TableRow>
<table.TableRow>
<table.TableCell>INV-002</table.TableCell>
<table.TableCell>Open</table.TableCell>
<table.TableCell>$80.00</table.TableCell>
</table.TableRow>
</table.TableBody>
<table.TableFooter>
<table.TableRow>
<table.TableCell attrs={gx.Attrs{{Key: "colspan", Value: "2"}}}>Total</table.TableCell>
<table.TableCell>$200.00</table.TableCell>
</table.TableRow>
</table.TableFooter>
</table.Table>Table: Selected
<table.Table>
<table.TableHeader>
<table.TableRow>
<table.TableHead>Invoice</table.TableHead>
<table.TableHead>Status</table.TableHead>
<table.TableHead>Amount</table.TableHead>
</table.TableRow>
</table.TableHeader>
<table.TableBody>
<table.TableRow attrs={gx.Attrs{{Key: "data-state", Value: "selected"}}}>
<table.TableCell>INV-001</table.TableCell>
<table.TableCell>Paid</table.TableCell>
<table.TableCell>$120.00</table.TableCell>
</table.TableRow>
<table.TableRow>
<table.TableCell>INV-002</table.TableCell>
<table.TableCell>Open</table.TableCell>
<table.TableCell>$80.00</table.TableCell>
</table.TableRow>
</table.TableBody>
</table.Table>TableBody: Empty
<table.TableBody children={gx.Frag()} />TableCaption: Caption
<table.TableCaption>A caption.</table.TableCaption>TableCell: Cell
<table.TableCell>Value</table.TableCell>TableFooter: Empty
<table.TableFooter children={gx.Frag()} />TableHead: Header
<table.TableHead>Column</table.TableHead>TableHeader: Empty
<table.TableHeader children={gx.Frag()} />TableRow: Empty
<table.TableRow children={gx.Frag()} />API reference
A tag sets a prop by its name with a lower-case first letter: Class is class.
table.Table
| Prop | Type | Default | Description |
|---|---|---|---|
Class |
string |
"" |
Class adds classes to the table element. |
Children |
gx.Node |
Required | Children is the content of the table: the caption, header, body and footer. |
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the table element. |
table.TableBody
| Prop | Type | Default | Description |
|---|---|---|---|
Class |
string |
"" |
Class adds classes to the root element. |
Children |
gx.Node |
Required | Children is the rows of the body. |
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the root element. |
table.TableCaption
| Prop | Type | Default | Description |
|---|---|---|---|
Class |
string |
"" |
Class adds classes to the root element. |
Children |
gx.Node |
Required | Children is the caption text. |
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the root element. |
table.TableCell
| Prop | Type | Default | Description |
|---|---|---|---|
Class |
string |
"" |
Class adds classes to the root element. |
Children |
gx.Node |
Required | Children is the content of the cell. |
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the root element. |
table.TableFooter
| Prop | Type | Default | Description |
|---|---|---|---|
Class |
string |
"" |
Class adds classes to the root element. |
Children |
gx.Node |
Required | Children is the rows of the footer. |
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the root element. |
table.TableHead
| Prop | Type | Default | Description |
|---|---|---|---|
Class |
string |
"" |
Class adds classes to the root element. |
Children |
gx.Node |
Required | Children is the content of the header cell. |
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the root element. |
table.TableHeader
| Prop | Type | Default | Description |
|---|---|---|---|
Class |
string |
"" |
Class adds classes to the root element. |
Children |
gx.Node |
Required | Children is the rows of the header. |
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the root element. |
table.TableRow
| Prop | Type | Default | Description |
|---|---|---|---|
Class |
string |
"" |
Class adds classes to the root element. |
Children |
gx.Node |
Required | Children is the cells of the row. |
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the root element. |
Do and do not
Do
- Put a
TableCaptionon every table. - Use
TableHeadfor header cells.
Don't
- Do not use a table for layout.
- Do not put a table inside a table head.
Keyboard
| Key | Action |
|---|---|
| None | The component is static. |