Display tabular data beautifully in a simple way.
This component like all other BladewindUI components is simple to use with a few options to customise the component to suit your app needs. A BladewindUI table consists of two parts. The table header and the table body.
Name | Department | |
---|---|---|
Alfred Rowe | Consulting | alfred@therowe.com |
Abigail Edwin | Quality Assurance | abigail@edwin.com |
Michael K. Ocansey | Development | kabutey@gmail.com |
John C. Doe | Virtual Reality | johncdoe@faked.com |
<x-bladewind::table>
<x-slot name="header">
<th>Name</th>
<th>Department</th>
<th>Email</th>
</x-slot>
<tr>
<td>Alfred Rowe</td>
<td>Outsourcing</td>
<td>alfred@therowe.com</td>
</tr>
<tr>
<td>Michael K. Ocansey</td>
<td>Tech</td>
<td>kabutey@gmail.com</td>
</tr>
</x-bladewind::table>
By default the table component does not display a border around the table. You can enable this by setting.
has_border="true"
Name | Department | |
---|---|---|
Alfred Rowe | Consulting | alfred@therowe.com |
Abigail Edwin | Quality Assurance | abigail@edwin.com |
Michael K. Ocansey | Development | kabutey@gmail.com |
John C. Doe | Virtual Reality | johncdoe@faked.com |
<x-bladewind::table
has_border="true">
...
</x-bladewind::table>
By default the BladewindUI table rows are displayed with wide gaps to place more emphasis on each row and it’s content. Each row also has a default hover effect that highlights the left and right borders of the row. These can both be turned off.
To remove the wide gaps between the table rows you need to set the divider attribute to thin, like this, divider="thin"
.
Name | Department | |
---|---|---|
Alfred Rowe | Consulting | alfred@therowe.com |
Abigail Edwin | Quality Assurance | abigail@edwin.com |
Michael K. Ocansey | Development | kabutey@gmail.com |
John C. Doe | Virtual Reality | johncdoe@faked.com |
<x-bladewind::table
divider="thin">
<x-slot name="header">
<th>Name</th>
...
</x-slot>
...
</x-bladewind::table>
It is also possible to completely turn off the divider lines.
To remove the divider completely, set the divided attribute to false, like this, divided="false"
.
Name | Department | |
---|---|---|
Alfred Rowe | Consulting | alfred@therowe.com |
Abigail Edwin | Quality Assurance | abigail@edwin.com |
Michael K. Ocansey | Development | kabutey@gmail.com |
John C. Doe | Virtual Reality | johncdoe@faked.com |
<x-bladewind::table
divided="false">
<x-slot name="header">
<th>Name</th>
...
</x-slot>
...
</x-bladewind::table>
To remove the beautiful green side border effect when users hover on each row, set the hover attribute to false, like this, hover_effect="false"
.
Name | Department | |
---|---|---|
Alfred Rowe | Consulting | alfred@therowe.com |
Abigail Edwin | Quality Assurance | abigail@edwin.com |
Michael K. Ocansey | Development | kabutey@gmail.com |
John C. Doe | Virtual Reality | johncdoe@faked.com |
<x-bladewind::table
hover_effect="false"
divider="thin">
<x-slot name="header">
<th>Name</th>
...
</x-slot>
...
</x-bladewind::table>
If the table feels too airy and spaced, there is a compact="true"
attribute to tighten things up.
Name | Department | |
---|---|---|
Alfred Rowe | Consulting | alfred@therowe.com |
Abigail Edwin | Quality Assurance | abigail@edwin.com |
Michael K. Ocansey | Development | kabutey@gmail.com |
John C. Doe | Virtual Reality | johncdoe@faked.com |
<x-bladewind::table
compact="true"
divider="thin">
<x-slot name="header">
<th>Name</th>
...
</x-slot>
...
</x-bladewind::table>
Design experts argue that it is sometimes easier for users to visually scan tabular data if the table has striped rows. We are not challenging the experts. We’ve however made it possible for you to make your BladewindUI tables have striped rows. Set striped="true"
on the table component to get a striped table.
Name | Department | |
---|---|---|
Alfred Rowe | Consulting | alfred@therowe.com |
Abigail Edwin | Quality Assurance | abigail@edwin.com |
Michael K. Ocansey | Development | kabutey@gmail.com |
John C. Doe | Virtual Reality | johncdoe@faked.com |
Jane Ama Doe | Virtual Reality | jane.doe@faked.com |
<x-bladewind::table
striped="true"
divider="thin">
<x-slot name="header">
<th>Name</th>
...
</x-slot>
...
</x-bladewind::table>
If you want your tables looking like an excel spreadsheet with each cell having all round borders, set celled="true"
.
Name | Department | Earnings | Tax | Amt. Due |
---|---|---|---|---|
Alfred Rowe | Consulting | 1,200 | 120 | 1,080 |
Abigail Edwin | Quality Assurance | 1,500 | 135 | 1,365 |
Michael K. Ocansey | Development | 1,390 | 125 | 1,265 |
John C. Doe | Virtual Reality | 1,100 | 98 | 98 |
<x-bladewind::table celled="true">
<x-slot name="header">
<th>Name</th>
...
</x-slot>
...
</x-bladewind::table>
Accountants have this interesting habit of double underlining their totals. If that’s something that interests you, apply the class double-underline
to the td
that holds the total value you want double underlined.
Item | Quantity | Price (GHS) |
---|---|---|
Office furniture | 2 | 4,300.00 |
HP Laser Jet Printer | 1 | 3,000.00 |
7,300.00 |
<x-bladewind::table striped="true" divider="thin">
<x-slot name="header">
<th>Name</th>
...
</x-slot>
...
<tr>
<td colspan="2" class="text-right"></td>
<td class="double-underline text-right">
7,300.00
</td>
</tr>
</x-bladewind::table>
You can add a subtle shadow effect to your BladewindUI tables by setting has_shadow="true"
Item | Quantity | Price (GHS) |
---|---|---|
Office furniture | 2 | 4,300.00 |
HP Laser Jet Printer | 1 | 3,000.00 |
7,300.00 |
<x-bladewind::table
has_shadow="true"
striped="true"
divider="thin">
<x-slot name="header">
<th>Name</th>
...
</x-slot>
...
</x-bladewind::table>
There are cases you may want to show users which rows they have selected. You can achieve that by setting selectable="true"
on the table.
Now, any time a user clicks on a row it will be selected. Clicking on multiple rows will select them all. Clicking on a row that is already selected will unselect it. The
cursor changes to cursor-pointer
when a table is defined as selectable. Cool shortcuts like Ctrl+Click or Shift+Click do not work here.
Item | Quantity | Unit Price (GHS) |
---|---|---|
Office furniture | 2 | 4,300.00 |
HP Laser Jet Printer | 1 | 3,000.00 |
Macbook Pro M3 (13'') | 4 | 24,300.00 |
iPhone 15 Pro Max | 12 | 19,000.00 |
Laptop Sleeve (Black) | 5 | 800.00 |
<x-bladewind::table selectable="true" divider="thin">
<x-slot name="header">
<th>Item</th>
<th>Quantity</th>
<th>Unit Price (GHS)</th>
</x-slot>
<tr>
<td>Office furniture</td>
<td class="text-center">2</td>
<td class="text-right">4,300.00</td>
</tr>
...
</x-bladewind::table>
We can take the selectable tables one step further by introducing checkboxes for every row. This is achieved by setting checkable="true"
on the table.
Each row should now have a checkbox injected as the first column. If a table heading exists, a checkbox is also injected as the first column in the table heading.
This becomes the master checkbox for checking or unchecking all other checkboxes at once. The state changes to a partial selected checkbox if some of the checkboxes are checked.
Item | Quantity | Unit Price (GHS) |
---|---|---|
Office furniture | 2 | 4,300.00 |
HP Laser Jet Printer | 1 | 3,000.00 |
Macbook Pro M3 (13'') | 4 | 24,300.00 |
iPhone 15 Pro Max | 12 | 19,000.00 |
Laptop Sleeve (Black) | 5 | 800.00 |
<x-bladewind::table selectable="true" checkable="true" divider="thin">
<x-slot name="header">
<th>Item</th>
<th>Quantity</th>
<th>Unit Price (GHS)</th>
</x-slot>
<tr>
<td>Office furniture</td>
<td class="text-center">2</td>
<td class="text-right">4,300.00</td>
</tr>
...
</x-bladewind::table>
Having selectable table rows definitely means you will want to do something with the selected values.
You will need to append unique IDs (data-id="your-uuid-value"
)
to your table rows in order for Bladewind to recognize and return them.
You will also need to provide a name for the table. This will be the name of the input field that holds the comma separated list of table row IDs that are selected.
<x-bladewind::table selectable="true" checkable="true" divider="thin"
name="office_supplies">
<x-slot name="header">
<th>Item</th>
<th>Quantity</th>
<th>Unit Price (GHS)</th>
</x-slot>
<tr data-id="1">
<td>Office furniture</td>
<td class="text-center">2</td>
<td class="text-right">4,300.00</td>
</tr>
<tr data-id="2">
<td>HP Laser Jet Printer</td>
<td class="text-center">2</td>
<td class="text-right">4,300.00</td>
</tr>
...
</x-bladewind::table>
We now have a hidden input injected on the page right after the table.
<input type="hidden" name="office_supplies" class="office_supplies" />
. We can now
access the value of this input field using JavaScript or via a form submission if your table is placed within a form.
In the example below we delete all table rows that are selected. Select more than one row.
Item | Quantity | Unit Price (GHS) |
---|---|---|
Office furniture | 2 | 4,300.00 |
HP Laser Jet Printer | 1 | 3,000.00 |
Macbook Pro M3 (13'') | 4 | 24,300.00 |
iPhone 15 Pro Max | 12 | 19,000.00 |
Laptop Sleeve (Black) | 5 | 800.00 |
<x-bladewind::card reduce_padding="true">
<!-- the delete button -->
<div class="office-supplies-actions p-3 bg-gray-100/50 rounded-lg hidden">
<x-bladewind::button size="tiny"
type="secondary" outline="true"
icon="trash" color="red"
onclick="deleteRows()">
Delete
</x-bladewind::button>
</div>
<x-bladewind::table selectable="true" divider="thin"
checkable="true"
name="office_supplies">
<x-slot:header>
<th>Item</th>
<th class="!text-center">Quantity</th>
<th class="!text-right">Unit Price (GHS)</th>
</x-slot:header>
<tr data-id="12">
<td>Office furniture</td>
<td class="text-center">2</td>
<td class="text-right">4,300.00</td>
</tr>
...
</x-bladewind::table>
</x-bladewind::card>
The above code will draw the table and let Bladewind do its checkboxing magic. You will however, need to write the code
for working with the values of the selected table rows. The deleteRows()
Javascript function below is what handles deleting of the
rows selected by the user from the example above. deleteRow()
is not a BladewindUI helper function.
// domEl(), domEls() and hide() are BladewindUI helper functions
deleteRows = () => {
// Our table is named 'office_supplies' so input.office_supplies is
// the hidden field Bladewind will write IDs of all selected rows to
const selectedRows = domEl('input.office_supplies').value.split(',');
// next we loop over all the rows of our 'office_supplies' table and
// hide any row that's having the value of its 'data-id' attribute
// in the selected rows array
const tableRows = domEls('table.office_supplies tr');
tableRows.forEach(row => {
if(selectedRows.indexOf(row.getAttribute('data-id')) !== -1) {
hide(row, true);
hide('.office-supplies-actions');
domEl('input.office_supplies').value = '';
}
});
}
selected_value
attribute on the table. This accepts a comma separated list of IDs.
<x-bladewind::table selectable="true" divider="thin"
checkable="true"
selected_value="2,4,19,23"
name="office_supplies">
...
There is no point manually building a table tediously when you have an array that contains everything you want to display as a table.
The table component has a data
attribute that accepts a json encoded array. The component builds its
table headings using the array keys.
Let us consider the array below and its resultant table.
$staff = [
[ 'id' => 1,
'first_name' => 'Michael',
'last_name' => 'Ocansey',
'department' => 'Engineering',
'marital_status' => 1
],
[
'id' => 2,
'first_name' => 'Alfred',
'last_name' => 'Rowe',
'department' => 'Engineering',
'marital_status' => 1
],
[
'id' => 3,
'first_name' => 'Abigail',
'last_name' => 'Edwin',
'department' => 'Engineering',
'marital_status' => 0
],
];
<x-bladewind::table :data="$staff" />
data
to the component. Note there is no colon before the data attribute and in this case the data is passed as a json encoded string.
<x-bladewind::table data="{{ json_encode($staff) }}" />
id | first name | last name | department | marital status |
---|---|---|---|---|
1 | Michael | Ocansey | Engineering | 1 |
2 | Alfred | Rowe | Engineering | 1 |
3 | Abigail | Edwin | Engineering | 0 |
As you can tell from the above example, the component simply picks the array passed to it and generates a table. You can apply all the table attributes to remove the hover effect, remove the gaps or even make the table striped.
By default, the table picks the keys of the array and generates its table headings.
Any key with underscores will be replaced with spaces. first_name becomes first name.
An API will return staff data with the ID of each user, but it is rare to see the IDs displayed in a table.
The table component allows you to exclude some columns using the exclude_columns
attribute.
There is also the include_columns
attribute that lets you specify the only columns to be displayed.
If you have an array with 20 fields and you want to display 5 out of the 20 fields, it will be easier to specify the 5 columns in the include_columns
attribute rather than specifying 15 columns in the exclude_columns
attribute.
Let us exclude id
and marital_status
from our table above.
include_columns
takes precedence over exclude_columns
. If you specify both attributes, exclude_columns
will be ignored.first name | last name | department |
---|
<x-bladewind::table
exclude_columns="id, marital_status"
:data="$staff" />
Using the table component with dynamic data allows you to specify some extra cool attributes.
You can show action icons by passing a json encoded array in the action_icons
attribute.
Each action icon can have the name of the icon, the icon colour (default is the secondary button colour), a tooltip and the click action of the icon.
The icons will be displayed in the order they are entered into the array.
$action_icons = [
"icon:chat | tip:send message | color:green | click:sendMessage('{first_name}')",
"icon:pencil | click:redirect('/user/{id}')",
"icon:trash | color:red | click:deleteUser({id}, '{first_name}')",
];
<x-bladewind::table
exclude_columns="id, marital_status"
divider="thin"
:action_icons="$action_icons"
:data="$staff" />
first name | last name | department | actions |
---|
The icons are displayed from the $action_icons
array above. Let us analyze the first line of the $action_icons
array.
Note how each attribute is separated by a pipe (|).
icon:chat-bubble-left | This will display the chat-bubble-left icon |
tip:send user a message | On hover of the icon, the user will see a tooltip that says "send user a message" |
click:sendMessage('{first_name}') | When the icon is clicked, the sendMessage Javascript function is triggered. The function name can be any function that exists in your code. Two parameters are passed to the function. The parameters can be any of the keys that exists in your array. In our earlier example our array contains the first_name key so we pass this to our function. It is important to wrap strings in a single quote. The keys also need to be wrapped in curly braces. The table component will replace '{first_name}' with the actual value of first_name from the array. |
color:green | The icon colour will be green. |
Below are the modals and Javascript functions being called when the icons are clicked. Mind you, the Javascript functions below are just for the documentation. THey need to be your own functions that you will call when the action icons are clicked.
<!-- send message modal -->
<x-bladewind.modal name="send-message" title="">
<div class="mb-6">
The message will be delivered to their company
inbox if they are not currently online
</div>
<x-bladewind.textarea
placeholder="Type message here..." rows="5" />
</x-bladewind.modal>
<!-- delete user modal -->
<x-bladewind.modal
name="delete-user"
type="error" title="Confirm User Deletion">
Are you really sure you want to delete <b class="title"></b>?
This action cannot be reversed.
</x-bladewind.modal>
sendMessage = (first_name, last_name) => {
showModal('send-message');
domEl('.bw-send-message .modal-title').innerText = `Send Message to ${first_name} ${last_name}`;
}
deleteUser = (id, first_name, last_name) => {
showModal('delete-user');
domEl('.bw-delete-user .title').innerText = `${first_name} ${last_name}`;
}
redirect = (url) => {
window.open(url);
}
When building dynamic table you will most likely be fetching your data from either an API or a database. You will barely be manually creating arrays as we did above.
Now with dynamic data, it is likely your API or query will return no records. You can display a custom translatable message in such a case. You can set the
no_data_message
to any message you wish to display.
The staff directory is empty |
<x-bladewind::table
no_data_message="The staff directory is empty"
:data="$staff" />
The dynamic table builds its column headings from the array keys defined in data
.
When there are no records to display, the array will be empty, thus, there will be no column headings to deduce. This is why there are no columns displayed with the
no data message.
If you wish to display column headings with the no data message, you will need to pass column_aliases
.
ref # | first name | last name | married? |
---|---|---|---|
The staff directory is empty |
$column_aliases = [
'id' => 'ref #',
'first_name' => 'first name',
'last_name' => 'last name',
'marital_status' => 'married?'
];
<x-bladewind::table
has_border="true"
no_data_message="The staff directory is empty"
:column_aliases="$column_aliases"
:data="$staff" />
Finally, it is possible to display the no data message using the Empty State component.
All the attributes of the component are allowed except message
and class
, since the table component already has its own class attribute,
and message here is already no_data_message
.
To display the message in an empty state, set the attribute message_as_empty_state="true"
.
ref # | first name | last name | married? |
---|---|---|---|
The staff directory is empty
|
<x-bladewind::table
:data="$no_staff"
has_border="true"
:column_aliases="$column_aliases"
no_data_message="The staff directory is empty"
message_as_empty_state="true"
button_label="add staff member" />
The table component provides a very basic way for users to search through table content.
If the searchable="true"
attribute is set,
a search field is placed above the table that makes it possible to search through any column of the table.
By placeholder text for the search input can be replaced by setting the search_placeholder
attribute on the table.
<x-bladewind::table
searchable="true"
:data="$staff"
divider="thin"
search_placeholder="Find staff members by name..."
:action_icons="$action_icons"
exclude_columns="id, marital_status" />
There are times your array might contain keys that are not user-friendly enough to be column headings. From our array above assuming we wanted to replace
marital_status
to married?
, we will set the column_aliases
attribute.
This attributes accepts a json encoded array.
$column_aliases = [
'id' => 'ref #',
'marital_status' => 'married?'
];
<x-bladewind::table
exclude_columns="id"
divider="thin"
:action_icons="$action_icons"
:column_aliases="$column_aliases"
:data="$staff" />
ref # | first name | last name | department | married? |
---|---|---|---|---|
1 | Michael | Ocansey | Engineering | 1 |
2 | Alfred | Rowe | Engineering | 1 |
3 | Abigail | Edwin | Engineering | 0 |
Let's assume you have a table of employees and wish to group these employees by department, so all staff in Marketing will be under the marketing heading and so on.
This can be achieved by specifying the groupby
attribute on the table.
The value of groupby
will need to be any of the keys in your array .
From the employee example above, we will set our grouping on the department
key which happens to be to repeated for our employees. groupby="department"
.
$staff = [
[
'id' => 1,
'first_name' => 'Michael',
'last_name' => 'Ocansey',
'department' => 'Engineering',
'email' => 'mike@email.com'
],
]
...
<x-bladewind::table
exclude_columns="id"
divider="thin"
groupby="department"
:data="$staff" />
ref # | first name | last name | |
---|---|---|---|
Engineering | |||
1 | Michael | Ocansey | mike@email.com |
2 | Alfred | Rowe | alfred@rowe.com |
3 | Abigail | Edwin | abi@edwin.com |
Sales | |||
4 | John | Doe | john@doe.com |
5 | Janet | Doe | jane@email.com |
6 | Michael | Sarpong | mike@sarpong.com |
The table below shows a comprehensive list of all the attributes available for the Table component.
Option | Default | Available Values |
---|---|---|
name | 'tbl-'.uniqid() | Name of the table. Useful if you want to target the table via Javascript. The name is added in the class="" attribute of the table. |
striped | false | Determines if the table rows are striped. Even rows get the stripes. The value should be passed as a string, not boolean. true false |
divided | true |
Determines if the table rows show the lines that divide them. The value should be passed as a string, not boolean. true false
|
divider | regular | Determines how wide the gaps are between table rows. regular thin |
hover_effect | false | Determines if the borders of the table rows light up when hovered over. The value should be passed as a string, not boolean.true false |
has_shadow | true | Determines if the table has a drop shadow effect. The value should be passed as a string, not boolean.true false |
compact | false | If set to true, the spacing between the TRs are reduced.true false |
header | blank | This slot holds your table header information. |
uppercasing | true | Determines if the table headings should be all uppercase. If false , the text will be displayed as you entered it. true false |
:data | null | Array of elements to generate the table from. When this has a value, there is no need to manually build the table. Ignore this attribute if you prefer to use data instead. |
data | null | Json encoded array of elements to generate the table from. When this has a value, there is no need to manually build the table. Ignore this attribute if you prefer to use :data instead.. |
exclude_columns | null | Comma separated list of columns to exclude when generating the table. The 'columns' need to match keys in your array. |
include_columns | null | Comma separated list of columns to include when generating the table. This list overwrites any columns specified in exclude_columns. In fact, the keys specified in this list will be the only ones used to generate the table. |
:action_icons | null |
Array of icon actions that will be displayed on each row of the table. Only used when data is not null. By default no action icons are displayed if value is null
This can also be passed as action_icons without the colon but then the array will need to be Json encoded.
|
action_title | actions | Heading of the column that shows the actions. |
:column_aliases | [] | Array of column aliases. Aliases are column names to use in place of what is defined in the data array. For example you may want a date_of_birth key to be displayed as birthday.
This can also be passed as column_aliases without the colon but then the array will need to be Json encoded. |
searchable | false | Specify if the table is searchable. When true , a search input is placed above the table.true false |
search_placeholder | Search table below... | Only used when searchable="true" . Specifies the text to display in the search input field. |
no_data_message | No records to display | Message to display when there is no dynamic data to display. |
message_as_empty_state | false | When there is no dynamic data to display, should the message be displayed as an empty state. true false |
image | empty-state.svg | Image to display in the empty state component when no dynamic data is available. |
heading | blank | Text to display as heading in empty state when no data is available for the dynamic table. |
button_label | blank | Label to display on empty state call to action button. |
celled | false | Display each cell with borders all round. Like you get in an excel sheet.true false |
show_image | true | Should the empty state component image be displayed.true false |
transparent | false | Should the empty table be transparent. ALl background colours are removed to enable the table sit well on any dark mode background colour.true false |
onclick | blank | Action to perform on the empty state component call to action button. Only used when displaying dynamic data. |
groupby | blank | Key of column to group rows by. The key needs to exist in the array you are displaying your data from. Works only for dynamic tables. |
selected_value | null | Comma separated list of row IDs to select when the table is rendered. |
<x-bladewind::table
striped="true"
divided="true"
divider="thin"
has_shadow="true"
has_border="true"
compact="true"
transparent="true"
searchable="false"
search_placeholder=""
name="staff-table"
:data="$data"
:column_aliases="$column_aliases"
include_columns="first_name, last_name, email"
exclude_columns="id,picture"
:action_icons="$action_icons"
action_title=""
no_data_message="The staff directory is empty"
message_as_empty_state="true"
button_label="add staff member"
image="asset('images/no-data.png')"
heading="No Staff"
groupby="department"
selected_value="2,3,4"
onclick="alert('add a staff')"
hover_effect="true">
<x-slot name="header">
...
</x-slot>
<tr>
...
</tr>
</x-bladewind::table>
resources > views > components > bladewind > table.blade.php