LiveTable (live_table v0.3.0)

View Source

A powerful Phoenix LiveView component library for building dynamic, interactive data tables with real-time updates. Perfect for admin panels, dashboards, and any application requiring advanced data presentation.

✨ Features

  • 🔍 Advanced Filtering - Text search, range filters, select dropdowns, boolean toggles
  • 📊 Smart Sorting - Multi-column sorting with shift-click support
  • 📄 Flexible Pagination - Configurable page sizes with efficient querying
  • 📤 Export Capabilities - CSV and PDF exports with background processing
  • ⚡ Real-time Updates - Built for Phoenix LiveView with instant feedback
  • 🎨 Multiple View Modes - Table and card layouts with custom components
  • 🔗 Custom Queries - Support for complex joins and computed fields
  • 🚀 Performance Optimized - Streams-based rendering for large datasets

LiveTable Demo

Live Demo with 1M+ records →

Advanced Demo with custom queries, & transformer usage →

Advanced Demo Git Url →

🚀 Quick Start

1. Installation

Add to your mix.exs:

def deps do
[
  {:live_table, "~> 0.3.0"},
  {:oban, "~> 2.19"}  # Required for exports
]
end

2. Basic Configuration

In your config/config.exs:

config :live_table,
repo: YourApp.Repo,
pubsub: YourApp.PubSub

# Configure Oban for exports
config :your_app, Oban,
repo: YourApp.Repo,
queues: [exports: 10]

3. Setup Assets

Add to assets/js/app.js:

import hooks_default from "../../deps/live_table/priv/static/live-table.js";

const liveSocket = new LiveSocket("/live", Socket, {
longPollFallbackMs: 2500,
params: { _csrf_token: csrfToken },
hooks: hooks_default,
});

Add to assets/css/app.css:

@source "../../deps/live_table/lib";

@import "../../deps/live_table/priv/static/live-table.css";

4. Create Your First Table

LiveTable requires field & filter definitions to build a table. Additional configuration options can be defined per table under table_options.

# lib/your_app_web/live/product_live/index.ex
defmodule YourAppWeb.ProductLive.Index do
use YourAppWeb, :live_view
use LiveTable.LiveResource, schema: YourApp.Product

def fields do
  [
    id: %{label: "ID", sortable: true},
    name: %{label: "Product Name", sortable: true, searchable: true},
    price: %{label: "Price", sortable: true},
    stock_quantity: %{label: "Stock", sortable: true}
  ]
end

def filters do
  [
    in_stock: Boolean.new(:stock_quantity, "in_stock", %{
      label: "In Stock Only",
      condition: dynamic([p], p.stock_quantity > 0)
    }),

    price_range: Range.new(:price, "price_range", %{
      type: :number,
      label: "Price Range",
      min: 0,
      max: 1000
    })
  ]
end
end

5. Add to Your Template

# lib/your_app_web/live/product_live/index.html.heex
<.live_table
fields={fields()}
filters={filters()}
options={@options}
streams={@streams}
/>

That's it! You now have a fully functional data table with sorting, filtering, pagination, and search.

🏗 Usage Patterns

Simple Tables (Single Schema)

For basic tables querying a single schema, use the schema: parameter. The field keys must match the schema field names exactly:

defmodule YourAppWeb.UserLive.Index do
use YourAppWeb, :live_view
use LiveTable.LiveResource, schema: YourApp.User

def fields do
  [
    id: %{label: "ID", sortable: true},        # Must match User.id field
    email: %{label: "Email", sortable: true, searchable: true},   # Must match User.email field
    name: %{label: "Name", sortable: true, searchable: true}      # Must match User.name field
  ]
end
end

Complex Tables (Custom Queries)

For tables with joins, computed fields, or complex logic, you must define a custom data provider. The field keys must match the keys in your query's select clause:

defmodule YourAppWeb.OrderReportLive.Index do
use YourAppWeb, :live_view
use LiveTable.LiveResource

def mount(_params, _session, socket) do
  # Assign your custom data provider
  socket = assign(socket, :data_provider, {YourApp.Orders, :list_with_details, []})
  {:ok, socket}
end

def fields do
  [
    order_id: %{label: "Order #", sortable: true},        # Must match select key
    customer_name: %{label: "Customer", sortable: true, searchable: true},  # Must match select key
    total_amount: %{label: "Total", sortable: true},      # Must match select key
    # For sorting by joined fields, specify the alias used in your query
    product_name: %{
      label: "Product",
      sortable: true,
      assoc: {:order_items, :name}    # Must match query alias and field
    }
  ]
end

def filters do
  [
    status: Select.new({:orders, :status}, "status", %{
      label: "Order Status",
      options: [
        %{label: "Pending", value: ["pending"]},
        %{label: "Completed", value: ["completed"]}
      ]
    })
  ]
end
end
# In your context
defmodule YourApp.Orders do
def list_with_details do
  from o in Order,
    join: c in Customer, on: o.customer_id == c.id,
    join: oi in OrderItem, on: oi.order_id == o.id, as: :order_items,
    select: %{
      order_id: o.id,               # Field key must match this
      customer_name: c.name,        # Field key must match this
      total_amount: o.total_amount, # Field key must match this
      product_name: oi.product_name # Field key must match this
    }
end
end

📚 Documentation

🎯 Use Cases

LiveTable is perfect for:

  • Admin Dashboards - Manage users, orders, products with advanced filtering
  • E-commerce Catalogs - Product listings with search, filters, and sorting
  • Data Analytics - Present large datasets with exports and real-time updates
  • CRM Systems - Customer and lead management with custom views
  • Inventory Management - Track stock with complex filtering and reporting

📄 License

MIT License. See LICENSE for details.

🤝 Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📞 Support


Built with ❤️ for the Phoenix LiveView community.