npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@loomidev/table

v0.3.0

Published

<loomi-table> — data table with sorting, search, pagination, checkable rows and action icons.

Downloads

58

Readme

@loomidev/table

<loomi-table> — a Loomi data table with manual rows, dynamic data, search, sorting, pagination (via <loomi-pagination>), selectable and checkable rows (via <loomi-checkbox>), row grouping, empty-state options, custom row templates and action icons.

npm install @loomidev/table lit
import "@loomidev/table";

Basic Usage

Pass an array of row objects via the .data property (or a JSON-string data attribute). Column headings are generated automatically from the first row's keys.

<loomi-table id="t"></loomi-table>

<script type="module">
  document.getElementById("t").data = [
    { first_name: "Ada", last_name: "Quint", department: "Engineering", email: "[email protected]" },
    { first_name: "Sara", last_name: "Field", department: "Design", email: "[email protected]" },
    { first_name: "Zane", last_name: "Hart", department: "Engineering", email: "[email protected]" },
    { first_name: "Rita", last_name: "Boon", department: "Marketing", email: "[email protected]" },
    { first_name: "Joe", last_name: "Diaz", department: "Design", email: "[email protected]" },
    { first_name: "Wren", last_name: "Ortiz", department: "Finance", email: "[email protected]" },
  ];
</script>

Styling Options

These toggles only show up once a table has enough rows to fill out — the same six-row staff list is reused across every variant below.

<loomi-table id="s1" striped></loomi-table>
<loomi-table id="s2" divided="false"></loomi-table>
<loomi-table id="s3" divider="thin"></loomi-table>
<loomi-table id="s4" has-hover></loomi-table>
<loomi-table id="s5" compact></loomi-table>
<loomi-table id="s6" celled></loomi-table>
<loomi-table id="s7" has-border has-shadow></loomi-table>

<script type="module">
  const staff = [
    { first_name: "Ada", last_name: "Quint", department: "Engineering", email: "[email protected]" },
    { first_name: "Sara", last_name: "Field", department: "Design", email: "[email protected]" },
    { first_name: "Zane", last_name: "Hart", department: "Engineering", email: "[email protected]" },
    { first_name: "Rita", last_name: "Boon", department: "Marketing", email: "[email protected]" },
    { first_name: "Joe", last_name: "Diaz", department: "Design", email: "[email protected]" },
    { first_name: "Wren", last_name: "Ortiz", department: "Finance", email: "[email protected]" },
  ];
  for (const id of ["s1", "s2", "s3", "s4", "s5", "s6", "s7"]) {
    document.getElementById(id).data = staff;
  }
</script>

| Toggle | Effect | | ----------------------- | ------------------------------------------ | | striped | Alternating row backgrounds. | | divided="false" | Removes row divider lines. | | divider="thin" | Thinner dividers. | | has-hover | Highlights rows on hover. | | compact | Tighter row padding. | | celled | Borders on every cell, like a spreadsheet. | | has-border has-shadow | Outer border and drop shadow. |

Choosing & Renaming Columns

By default every key on the first row becomes a column. Narrow that down with include-columns (takes priority) or exclude-columns, and rename headings with column-aliases.

<loomi-table id="t2" exclude-columns="id,email"></loomi-table>

<script type="module">
  const t = document.getElementById("t2");
  t.data = [
    { id: 1, first_name: "Ada", department: "Engineering", email: "[email protected]" },
    { id: 2, first_name: "Sara", department: "Design", email: "[email protected]" },
    { id: 3, first_name: "Zane", department: "Engineering", email: "[email protected]" },
    { id: 4, first_name: "Rita", department: "Marketing", email: "[email protected]" },
  ];
  t.columnAliases = { first_name: "Name", department: "Team" };
</script>

Action Icons

Pass an array of { icon, name, tip, color } objects via action-icons to add a column of icon buttons. Listen for the loomi-action event to handle clicks — e.detail is { name, row }, so you always get the full row data for that line.

Each icon renders as a circular outline button in secondary by default; pass color to use a different loomi color instead. Set buttonOutline: false to render a solid filled circle instead of the outline.

<loomi-table id="t3"></loomi-table>

<script type="module">
  const t = document.getElementById("t3");
  t.data = [
    { first_name: "Ada", department: "Engineering" },
    { first_name: "Sara", department: "Design" },
    { first_name: "Zane", department: "Engineering" },
    { first_name: "Rita", department: "Marketing" },
  ];
  t.actionIcons = [
    { icon: "paper-airplane", name: "message", tip: "Message", color: "success" },
    { icon: "trash", name: "delete", tip: "Delete", color: "error" },
  ];
  t.addEventListener("action", (e) => {
    console.log(e.detail.name, e.detail.row); // "delete", { first_name: "Ada", ... }
  });
</script>

Row Click Handler

Listen for loomi-row-click to react to a click anywhere on a row (clicks on the action-icon cell don't trigger it, so icon clicks and row clicks never collide).

t.addEventListener("loomi-row-click", (e) => goToProfile(e.detail.row.id));

Selectable Rows

Set selectable to let row clicks toggle selected state. This uses the same selection store as checkboxes, so selectedIds, selectedValue, and loomi-selection-change all stay in sync.

<loomi-table id="selectable-staff" selectable></loomi-table>

<script type="module">
  document.getElementById("selectable-staff").data = [
    { id: 1, first_name: "Ada", department: "Engineering", email: "[email protected]" },
    { id: 2, first_name: "Sara", department: "Design", email: "[email protected]" },
    { id: 3, first_name: "Zane", department: "Engineering", email: "[email protected]" },
    { id: 4, first_name: "Rita", department: "Marketing", email: "[email protected]" },
    { id: 5, first_name: "Joe", department: "Design", email: "[email protected]" },
  ];
</script>

Searchable

<loomi-table id="t4" searchable search-placeholder="Find staff members by name…"></loomi-table>

<script type="module">
  document.getElementById("t4").data = [
    { first_name: "Ada", last_name: "Quint", department: "Engineering", email: "[email protected]" },
    { first_name: "Sara", last_name: "Field", department: "Design", email: "[email protected]" },
    { first_name: "Zane", last_name: "Hart", department: "Engineering", email: "[email protected]" },
    { first_name: "Rita", last_name: "Boon", department: "Marketing", email: "[email protected]" },
    { first_name: "Joe", last_name: "Diaz", department: "Design", email: "[email protected]" },
    { first_name: "Nia", last_name: "Cole", department: "Marketing", email: "[email protected]" },
  ];
</script>

The search box filters across every visible column's stringified value, client-side — typing "design" above narrows the six rows down to Sara and Joe.

Search Placement

searchable renders a full-width search field above the table by default. Set search-container to a DOM selector when the search field should live somewhere else on the page.

<div id="staff-search"></div>
<loomi-table id="staff-table" searchable search-container="#staff-search"></loomi-table>

Sortable

<loomi-table id="t5" sortable></loomi-table>

<!-- restrict which columns can be sorted -->
<loomi-table id="t6" sortable sortable-columns="first_name,department"></loomi-table>

<script type="module">
  const staff = [
    { first_name: "Zane", last_name: "Hart", department: "Engineering", email: "[email protected]" },
    { first_name: "Ada", last_name: "Quint", department: "Engineering", email: "[email protected]" },
    { first_name: "Rita", last_name: "Boon", department: "Marketing", email: "[email protected]" },
    { first_name: "Joe", last_name: "Diaz", department: "Design", email: "[email protected]" },
    { first_name: "Nia", last_name: "Cole", department: "Marketing", email: "[email protected]" },
    { first_name: "Sara", last_name: "Field", department: "Design", email: "[email protected]" },
  ];
  document.getElementById("t5").data = staff;
  document.getElementById("t6").data = staff;
</script>

Click a sortable column heading to sort by it; click again to reverse direction.

Checkable Rows

Adds a checkbox column. Read the current selection from the selectedIds property, or listen for loomi-selection-change.

<loomi-table id="t7" checkable></loomi-table>

<script type="module">
  const t7 = document.getElementById("t7");
  t7.data = [
    { id: 1, first_name: "Ada", department: "Engineering", email: "[email protected]" },
    { id: 2, first_name: "Sara", department: "Design", email: "[email protected]" },
    { id: 3, first_name: "Zane", department: "Engineering", email: "[email protected]" },
    { id: 4, first_name: "Rita", department: "Marketing", email: "[email protected]" },
    { id: 5, first_name: "Joe", department: "Design", email: "[email protected]" },
  ];
  t7.addEventListener("loomi-selection-change", (e) => {
    console.log(e.detail.ids); // every checked row's id
  });
</script>

Pre-check rows on load with selected-value (comma-separated ids), and control which field counts as the row's id with id-key (defaults to id).

Grouping Rows

Group dynamic rows by any key in your data with groupby. Rows that share a value are collected under a heading row for that value, in the order the groups first appear.

<loomi-table id="staff-by-team" groupby="department"></loomi-table>

<script type="module">
  document.getElementById("staff-by-team").data = [
    { first_name: "Ada", last_name: "Quint", department: "Engineering", email: "[email protected]" },
    { first_name: "Zane", last_name: "Hart", department: "Engineering", email: "[email protected]" },
    { first_name: "Leo", last_name: "Martin", department: "Engineering", email: "[email protected]" },
    { first_name: "Priya", last_name: "Nair", department: "Engineering", email: "[email protected]" },
    { first_name: "Sara", last_name: "Field", department: "Design", email: "[email protected]" },
    { first_name: "Joe", last_name: "Diaz", department: "Design", email: "[email protected]" },
    { first_name: "Rita", last_name: "Boon", department: "Marketing", email: "[email protected]" },
    { first_name: "Nia", last_name: "Cole", department: "Marketing", email: "[email protected]" },
    { first_name: "Wren", last_name: "Ortiz", department: "Finance", email: "[email protected]" },
    { first_name: "Tom", last_name: "Brandt", department: "Finance", email: "[email protected]" },
  ];
</script>

This renders four group headings — Engineering, Design, Marketing, Finance — each followed by its rows.

Pagination

<loomi-table id="t8" paginated page-size="5"></loomi-table>

<!-- show page numbers instead of prev/next arrows -->
<loomi-table id="t9" paginated page-size="5" pagination-style="numbers"></loomi-table>

<!-- use a page dropdown -->
<loomi-table id="t9-dropdown" paginated page-size="5" pagination-style="dropdown"></loomi-table>

<!-- leading row-number column -->
<loomi-table id="t10" paginated page-size="5" show-row-numbers></loomi-table>

<script type="module">
  const staff = [
    { id: 1, first_name: "Ada", department: "Engineering", email: "[email protected]" },
    { id: 2, first_name: "Sara", department: "Design", email: "[email protected]" },
    { id: 3, first_name: "Zane", department: "Engineering", email: "[email protected]" },
    { id: 4, first_name: "Rita", department: "Marketing", email: "[email protected]" },
    { id: 5, first_name: "Joe", department: "Design", email: "[email protected]" },
    { id: 6, first_name: "Nia", department: "Marketing", email: "[email protected]" },
    { id: 7, first_name: "Leo", department: "Engineering", email: "[email protected]" },
    { id: 8, first_name: "Wren", department: "Finance", email: "[email protected]" },
    { id: 9, first_name: "Priya", department: "Engineering", email: "[email protected]" },
    { id: 10, first_name: "Tom", department: "Finance", email: "[email protected]" },
    { id: 11, first_name: "Maya", department: "Support", email: "[email protected]" },
    { id: 12, first_name: "Owen", department: "Support", email: "[email protected]" },
  ];
  for (const id of ["t8", "t9", "t9-dropdown", "t10"]) document.getElementById(id).data = staff;
</script>

With page-size="5" and twelve rows, each table above paginates across three pages — enough to see the arrow, numbers, and row-number variants actually flip between pages. Pagination styles: arrows (default), numbers, dropdown — same options as <loomi-pagination>, since that's exactly what renders underneath.

Customize the total text with :a, :b, and :c placeholders:

<loomi-table paginated page-size="5" total-label=":a - :b of :c"></loomi-table>
<loomi-table paginated page-size="5" total-label="Showing :a - :b"></loomi-table>

Those render as 1 - 5 of 50 and Showing 1 - 5.

No Data Message

<loomi-table id="t11" no-data-message="The staff directory is empty"></loomi-table>

Render the message as an empty-state panel with optional image, heading, and button:

<loomi-table
  message-as-empty-state
  show-image="false"
  heading="No staff"
  no-data-message="The staff directory is empty"
  button-label="Add staff"
></loomi-table>

Listen for loomi-empty-action to handle the empty-state button. The onclick attribute is accepted and included in the event detail.

Manual and Custom Layouts

For a manually authored table, provide header cells in the header slot and rows in the default slot:

<loomi-table selectable>
  <th slot="header">Item</th>
  <th slot="header">Quantity</th>
  <tr><td>Office furniture</td><td>2</td></tr>
  <tr><td>Standing desks</td><td>6</td></tr>
  <tr><td>Monitor arms</td><td>10</td></tr>
</loomi-table>

For dynamic data with a custom row layout, set layout="custom" and provide header and row templates. Row templates replace {key} placeholders from each row, while pagination still works from the data array.

<loomi-table id="custom-users" layout="custom" paginated page-size="5">
  <template slot="header"><th>ID</th><th>User Details</th></template>
  <template slot="row">
    <tr><td>{id}</td><td><strong>{name}</strong><br>{email}</td></tr>
  </template>
</loomi-table>

<script type="module">
  document.getElementById("custom-users").data = [
    { id: 1, name: "Ada Quint", email: "[email protected]" },
    { id: 2, name: "Sara Field", email: "[email protected]" },
    { id: 3, name: "Zane Hart", email: "[email protected]" },
    { id: 4, name: "Rita Boon", email: "[email protected]" },
    { id: 5, name: "Joe Diaz", email: "[email protected]" },
    { id: 6, name: "Nia Cole", email: "[email protected]" },
    { id: 7, name: "Leo Martin", email: "[email protected]" },
  ];
</script>

Putting It Together

Search, sort, checkable rows, pagination, and action icons all compose freely on the same table:

<loomi-table id="full-table" searchable sortable paginated page-size="5" checkable has-hover></loomi-table>

<script type="module">
  const t = document.getElementById("full-table");
  t.data = [
    { id: 1, first_name: "Ada", department: "Engineering" },
    { id: 2, first_name: "Sara", department: "Design" },
    { id: 3, first_name: "Zane", department: "Engineering" },
    { id: 4, first_name: "Rita", department: "Marketing" },
    { id: 5, first_name: "Joe", department: "Design" },
    { id: 6, first_name: "Nia", department: "Marketing" },
    { id: 7, first_name: "Leo", department: "Engineering" },
  ];
  t.excludeColumns = "id";
  t.actionIcons = [{ icon: "trash", name: "delete", color: "error", tip: "Delete" }];
  t.addEventListener("action", (e) => console.log(e.detail));
  t.addEventListener("loomi-selection-change", (e) => console.log(e.detail.ids));
</script>

Accessibility

For the library-wide baseline, see Foundations — Accessibility.

Responsive behavior

For the shared container and viewport rules, see Foundations — Responsive behavior.

Dark mode

loomi-table uses Loomi semantic tokens such as --loomi-surface, --loomi-surface-muted, --loomi-surface-border-subtle, --loomi-text, and palette accent tokens instead of hard-coded light colors. The table header, row dividers, panels, hover states, and muted text shift with the active theme preset.

For theme activation, token overrides, and contrast guidance, see Foundations — Dark mode.

Attributes

| Attribute | Default | Description | | ------------------------ | -------------------------------- | ------------------------------------------------------------------------------------- | | name | auto | Stable class/name hook, matching Loomi targeting patterns. | | data | [] | Row objects — property (.data) or JSON-string attribute. | | columns | (auto) | Column keys (defaults to the first row's keys). | | layout | auto | auto | custom; custom uses row/header templates or slotted rows. | | row-template | (blank) | Template string for layout="custom"; {key} placeholders are filled from row data. | | include-columns | (blank) | Comma-separated key allow list. | | exclude-columns | (blank) | Comma-separated key deny list (include wins if both are set). | | column-aliases | {} | Map of key -> display name (property or JSON). | | searchable | false | Show a search box. (boolean) | | search-container | (blank) | DOM selector where the search input should be rendered. | | search-placeholder | Search table below... | Search input placeholder. | | sortable | false | Enable column sorting. (boolean) | | sortable-columns | (all) | Comma-separated sortable keys. | | paginated | false | Enable pagination. (boolean) | | page-size | 25 | Rows per page. | | pagination-style | arrows | arrows | numbers | dropdown | | show-total | true | Show the pagination total label. (boolean) | | show-page-number | true | Show current page between arrow controls. (boolean) | | show-total-pages | false | Show current / total for arrow pagination. (boolean) | | default-page | 1 | Initial selected page. | | limit | 0 | Max total rows to display (0 = no limit). | | total-label | Showing :a to :b of :c records | Pagination total label placeholders. | | selectable | false | Row clicks toggle selection. (boolean) | | checkable | false | Add a checkbox column. (boolean) | | id-key | id | Row key used as the selection id. | | selected-value | (blank) | Comma-separated ids to pre-check. | | action-icons | [] | Array of { icon, name?, tip?, color?, click?, iconType?, buttonOutline? }. | | actions-title | actions | Heading for the action-icons column. | | show-row-numbers | false | Show a leading # column. (boolean) | | groupby | (blank) | Key used to render group heading rows. | | striped | false | Alternating row backgrounds. (boolean) | | divided | true | Row divider lines. (boolean) | | celled | false | Borders on every cell, like a spreadsheet. (boolean) | | compact | false | Tighter row padding. (boolean) | | transparent | false | Removes the table's own background. (boolean) | | has-hover | false | Highlights rows on hover. (boolean) | | has-shadow | true | Outer drop shadow. (boolean) | | has-border | false | Outer border. (boolean) | | divider | regular | regular | thin | | no-data-message | No records to display | Shown when there are no rows. | | message-as-empty-state | false | Render no-data content as an empty state. (boolean) | | image | empty-state.svg | Empty-state image URL. | | heading | (blank) | Empty-state heading. | | button-label | (blank) | Empty-state CTA label. | | show-image | true | Show empty-state image. (boolean) | | onclick | (blank) | empty-state action string, also emitted in loomi-empty-action. | | nonce | (blank) | Accepted as a no-op compatibility attribute. |

Properties: selectedIds, selectedRows (read-only current selection).

Slots

| Slot | Description | | ----------- | ------------------------------------ | | (default) | Content placed inside the component. | | header | Custom header content. |

Events

| Event | Description | | ------------------------ | ------------------------------------------------ | | loomi-action | Fired when a configured row action is activated. | | loomi-action-call | Fired when an action-cell callback is requested. | | loomi-empty-action | Fired when the empty-state action is activated. | | loomi-page-change | Fired when the page changes. | | loomi-row-click | Fired when a row is activated. | | loomi-selection-change | Fired when the selection changes. |

Full Example

<loomi-table
  id="staff-table"
  striped
  divided
  divider="thin"
  has-shadow
  has-border
  compact
  searchable
  search-placeholder="Search staff…"
  sortable
  checkable
  paginated
  page-size="25"
  pagination-style="numbers"
  show-row-numbers
  no-data-message="The staff directory is empty"
></loomi-table>

<script type="module">
  const t = document.getElementById("staff-table");
  t.data = [
    { id: 1, first_name: "Ada", last_name: "Quint", department: "Engineering", email: "[email protected]" },
    { id: 2, first_name: "Sara", last_name: "Field", department: "Design", email: "[email protected]" },
    { id: 3, first_name: "Zane", last_name: "Hart", department: "Engineering", email: "[email protected]" },
    { id: 4, first_name: "Rita", last_name: "Boon", department: "Marketing", email: "[email protected]" },
    { id: 5, first_name: "Joe", last_name: "Diaz", department: "Design", email: "[email protected]" },
    { id: 6, first_name: "Nia", last_name: "Cole", department: "Marketing", email: "[email protected]" },
    { id: 7, first_name: "Leo", last_name: "Martin", department: "Engineering", email: "[email protected]" },
    { id: 8, first_name: "Wren", last_name: "Ortiz", department: "Finance", email: "[email protected]" },
    { id: 9, first_name: "Priya", last_name: "Nair", department: "Engineering", email: "[email protected]" },
    { id: 10, first_name: "Tom", last_name: "Brandt", department: "Finance", email: "[email protected]" },
  ];
  t.excludeColumns = "id";
  t.columnAliases = { first_name: "First Name", last_name: "Last Name" };
  t.actionIcons = [
    { icon: "pencil-square", name: "edit", tip: "Edit" },
    { icon: "trash", name: "delete", color: "error", tip: "Delete" },
  ];
</script>

Framework integration

<loomi-table> is a standard custom element, so the browser can use it in plain HTML, Blade, React, Vue, Angular, Svelte, Astro, and most other frameworks. The important beginner rule is: install the package, import it once before the tag is rendered, then write the Loomi tag in your template.

Where to run commands

Run install commands from the app where you want to use this component. That means the folder that contains that app's package.json. Do not run these install commands from packages/table unless you are editing LoomiUI itself.

cd /path/to/your-app
npm install @loomidev/table lit

If you are contributing to LoomiUI itself, first move to the top-level components folder. That is where the main package.json for all packages lives, and pnpm --filter ... commands should be run from there:

cd /path/to/your-copy-of-loomiui/components
pnpm --filter @loomidev/table build
pnpm --filter @loomidev/table typecheck

Choose your framework

Use the CDN version for prototypes, documentation pages, or a quick reproduction. The import map tells the browser where to find Lit, which Loomi components use internally.

<script type="importmap">
  { "imports": { "lit": "https://esm.sh/[email protected]", "lit/": "https://esm.sh/[email protected]/" } }
</script>
<script type="module" src="https://esm.sh/@loomidev/table"></script>

<loomi-table id="customers-table" searchable sortable paginated page-size="10"></loomi-table>

In Vite, Webpack, Parcel, Rollup, or a framework build pipeline, install the package and import it once in your main app JavaScript file. After that, you can use the Loomi tag anywhere in your app.

import "@loomidev/table";

This component accepts data as a JavaScript property. Use an HTML attribute only for simple strings; use a property when you pass arrays, objects, or functions.

const el = document.querySelector("loomi-table");
el.data = [{ id: 1, name: "Emma", email: "[email protected]" }, { id: 2, name: "Chris", email: "[email protected]" }];

Run the install command from your Laravel project root, then import the component in resources/js/app.js. If your project uses Laravel Vite, npm run dev and npm run build should also be run from the Laravel project root.

cd /path/to/your-laravel-app
npm install @loomidev/table lit
npm run dev
// resources/js/app.js
import "@loomidev/table";
<loomi-table id="customers-table" searchable sortable paginated page-size="10"></loomi-table>

React can render Loomi tags directly. If you are on React 18, or if you need to pass arrays, objects, or functions, use a ref and assign those values after the component mounts.

import { useEffect, useRef } from "react";
import "@loomidev/table";

export function LoomiExample() {
  const el = useRef(null);

  useEffect(() => {
    el.current.data = [{ id: 1, name: "Emma", email: "[email protected]" }, { id: 2, name: "Chris", email: "[email protected]" }];
  }, []);

  return <loomi-table ref={el}></loomi-table>;
}

If TypeScript does not recognize the Loomi tag in JSX, add it to your app's JSX type declarations.

Import the package in the component that uses it, or once in your main Vue file. Vue templates can use Loomi tags directly. For arrays, objects, or functions, pass the value as a JavaScript property instead of as plain text.

<script setup>
import { onMounted, ref } from "vue";
import "@loomidev/table";

const el = ref(null);

onMounted(() => {
  el.value.data = [{ id: 1, name: "Emma", email: "[email protected]" }, { id: 2, name: "Chris", email: "[email protected]" }];
});
</script>

<template>
  <loomi-table ref="el"></loomi-table>
</template>

If Vue warns that the tag is an unknown component, configure compilerOptions.isCustomElement for tags that start with loomi- in your Vite or Vue config.

Import the package once and tell Angular to allow custom HTML tags with CUSTOM_ELEMENTS_SCHEMA. For NgModule apps, add the schema to the module instead of the standalone component.

// app.component.ts
import { AfterViewInit, CUSTOM_ELEMENTS_SCHEMA, Component, ElementRef, ViewChild } from "@angular/core";
import "@loomidev/table";

@Component({
  selector: "app-root",
  standalone: true,
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `
    <loomi-table #el></loomi-table>
  `,
})
export class AppComponent implements AfterViewInit {
  @ViewChild("el") el!: ElementRef<any>;

  ngAfterViewInit() {
    this.el.nativeElement.data = [{ id: 1, name: "Emma", email: "[email protected]" }, { id: 2, name: "Chris", email: "[email protected]" }];
  }
}

Svelte can import the package inside a component script. Astro can import it in the frontmatter of the page or layout where the tag appears.

<script>
  import { onMount } from "svelte";
  import "@loomidev/table";

  let el;

  onMount(() => {
    el.data = [{ id: 1, name: "Emma", email: "[email protected]" }, { id: 2, name: "Chris", email: "[email protected]" }];
  });
</script>

<loomi-table bind:this={el}></loomi-table>
---
import "@loomidev/table";
---

<loomi-table id="customers-table" searchable sortable paginated page-size="10"></loomi-table>

Server-side rendering notes

Frameworks such as Next.js, Nuxt, SvelteKit, and Astro sometimes render HTML on the server before browser-only code runs. If your framework complains, move the Loomi import to client-side code. In Next.js, that usually means a component with "use client"; in Nuxt, it often means a .client.ts plugin.

Dependencies

  • @loomidev/checkbox
  • @loomidev/core
  • @loomidev/icons
  • @loomidev/input
  • @loomidev/pagination