Skip to content

Icons

Inertia Table renders icons in cells via withIcon() and asIcon() modifiers. The icon name is defined in PHP - the frontend resolves it to an actual component.

PHP Usage

withIcon - Icon Alongside Content

Renders an icon before other display types. When used with a badge, the icon renders inside the badge.

php
// Fixed icon name
Column::make('name', 'Name')->withIcon('user')->text()

// Map column values to icon names (with fallback)
Column::make('type', 'Type')
    ->withIcon(['server' => 'server', 'site' => 'globe'], default: 'box')
    ->text()

// Closure for dynamic logic
Column::make('type', 'Type')
    ->withIcon(fn ($m) => $m->priority > 5 ? 'alert-triangle' : 'info')
    ->text()

// With a badge - icon renders inside the badge
Column::make('status', 'Status')
    ->withIcon(['active' => 'check', 'failed' => 'x'])
    ->badge(colorField: 'status_color')

asIcon - Icon Only

Renders only the icon - all other display types are ignored. The column header still shows.

php
// Map column values to icon names
Column::make('type', 'Type')
    ->asIcon(['server' => 'server', 'site' => 'globe'], default: 'box')

// Closure
Column::make('type', 'Type')
    ->asIcon(fn ($m) => $m->type === 'server' ? 'server' : 'x')

// Fixed icon
Column::make('status', 'Status')->asIcon('check')->fit()

Parameters

Both withIcon() and asIcon() accept the same parameters:

ParameterTypeDescription
$iconstring|array|ClosureFixed icon name, value-to-icon map, or closure
$default?stringFallback icon when using a map (default: null)

When using a map, the keys are matched against the column's value. For enum columns, the match uses the enum's getText() value.

Frontend Setup

There are two ways to provide icons on the frontend: an icon resolver (recommended) or the global registry.

Pass an iconResolver prop to resolve icon names to components dynamically. No manual registration needed.

tsx
import { InertiaTable } from 'inertia-table-react';
import { icons } from 'lucide-react';

function resolveLucideIcon(name: string) {
    // "server" -> "Server", "arrow-up" -> "ArrowUp"
    const pascalName = name.replace(/(^|-)([a-z])/g, (_, __, c) => c.toUpperCase());
    return icons[pascalName] ?? null;
}

export default function Servers({ servers }) {
    return (
        <InertiaTable
            tableData={servers}
            iconResolver={resolveLucideIcon}
        />
    );
}
tsx
import { InertiaTable } from 'inertia-table-react';
import * as OutlineIcons from '@heroicons/react/24/outline';

function resolveHeroicon(name: string) {
    // "server" -> "ServerIcon", "globe-alt" -> "GlobeAltIcon"
    const pascalName = name.replace(/(^|-)([a-z])/g, (_, __, c) => c.toUpperCase()) + 'Icon';
    return OutlineIcons[pascalName] ?? null;
}

export default function Servers({ servers }) {
    return (
        <InertiaTable
            tableData={servers}
            iconResolver={resolveHeroicon}
        />
    );
}
vue
<script setup lang="ts">
import { InertiaTable } from 'inertia-table-vue';
import * as icons from 'lucide-vue-next';

function resolveLucideIcon(name: string) {
    // "server" -> "Server", "arrow-up" -> "ArrowUp"
    const pascalName = name.replace(/(^|-)([a-z])/g, (_, __, c) => c.toUpperCase());
    return icons[pascalName] ?? null;
}

defineProps<{ servers: any }>();
</script>

<template>
    <InertiaTable
        :table-data="servers"
        :icon-resolver="resolveLucideIcon"
    />
</template>
vue
<script setup lang="ts">
import { InertiaTable } from 'inertia-table-vue';
import * as OutlineIcons from '@heroicons/vue/24/outline';

function resolveHeroicon(name: string) {
    // "server" -> "ServerIcon", "globe-alt" -> "GlobeAltIcon"
    const pascalName = name.replace(/(^|-)([a-z])/g, (_, __, c) => c.toUpperCase()) + 'Icon';
    return OutlineIcons[pascalName] ?? null;
}

defineProps<{ servers: any }>();
</script>

<template>
    <InertiaTable
        :table-data="servers"
        :icon-resolver="resolveHeroicon"
    />
</template>

The resolver receives the icon name string from PHP and returns a component or null:

tsx
type IconResolver = (name: string) => ComponentType<{ className?: string }> | null | undefined;

If the resolver returns null, the global registry is checked as a fallback.

Shared Resolver

To avoid passing iconResolver on every table, define it once and reuse:

tsx
// lib/icons.ts
import { icons } from 'lucide-react';

export function iconResolver(name: string) {
    const pascalName = name.replace(/(^|-)([a-z])/g, (_, __, c) => c.toUpperCase());
    return icons[pascalName] ?? null;
}

// Pages/Servers.tsx
import { iconResolver } from '@/lib/icons';

<InertiaTable tableData={servers} iconResolver={iconResolver} />
tsx
// lib/icons.ts
import * as OutlineIcons from '@heroicons/react/24/outline';

export function iconResolver(name: string) {
    const pascalName = name.replace(/(^|-)([a-z])/g, (_, __, c) => c.toUpperCase()) + 'Icon';
    return OutlineIcons[pascalName] ?? null;
}

// Pages/Servers.tsx
import { iconResolver } from '@/lib/icons';

<InertiaTable tableData={servers} iconResolver={iconResolver} />
vue
// lib/icons.ts
import * as icons from 'lucide-vue-next';

export function iconResolver(name: string) {
    const pascalName = name.replace(/(^|-)([a-z])/g, (_, __, c) => c.toUpperCase());
    return icons[pascalName] ?? null;
}

// Pages/Servers.vue
<script setup lang="ts">
import { InertiaTable } from 'inertia-table-vue';
import { iconResolver } from '@/lib/icons';

defineProps<{ servers: any }>();
</script>

<template>
    <InertiaTable :table-data="servers" :icon-resolver="iconResolver" />
</template>
vue
// lib/icons.ts
import * as OutlineIcons from '@heroicons/vue/24/outline';

export function iconResolver(name: string) {
    const pascalName = name.replace(/(^|-)([a-z])/g, (_, __, c) => c.toUpperCase()) + 'Icon';
    return OutlineIcons[pascalName] ?? null;
}

// Pages/Servers.vue
<script setup lang="ts">
import { InertiaTable } from 'inertia-table-vue';
import { iconResolver } from '@/lib/icons';

defineProps<{ servers: any }>();
</script>

<template>
    <InertiaTable :table-data="servers" :icon-resolver="iconResolver" />
</template>

Global Registry

Alternatively, register icons by name at app boot. These are available to all tables without passing a prop:

tsx
// app.tsx
import { registerIcons, registerIcon } from 'inertia-table-react';
import { Server, Database, Globe } from 'lucide-react';

// Bulk register
registerIcons({ server: Server, database: Database, globe: Globe });

// Or one at a time
registerIcon('server', Server);
ts
// app.ts
import { registerIcons, registerIcon } from 'inertia-table-vue';
import { Server, Database, Globe } from 'lucide-vue-next';

// Bulk register
registerIcons({ server: Server, database: Database, globe: Globe });

// Or one at a time
registerIcon('server', Server);

No iconResolver prop needed - the table looks up icons from the registry automatically:

tsx
<InertiaTable tableData={servers} />

The trade-off is that you must register each icon you use. The resolver approach is more flexible since it handles any name automatically.

Resolution Order

When the frontend encounters an icon name:

  1. iconResolver prop - checked first (if provided)
  2. Global registry - checked as fallback
  3. Not found - the cell renders nothing

This means you can use both together - the resolver handles most icons, and the registry provides overrides for specific names.

Badge Icons

When withIcon() is used with a badge() display, the icon renders inside the badge component (before the text). This is handled automatically - no extra frontend configuration needed.

php
// PHP
Column::make('status', 'Status')
    ->withIcon(['active' => 'check', 'failed' => 'x-circle', 'pending' => 'clock'])
    ->badge(colorField: 'status_color')

The badge renders as: [icon] Active with the icon sized to match the badge text.

Icon Styling

Standalone icons render with className="h-4 w-4 text-gray-500 dark:text-gray-400". Icons inside badges render with className="h-3 w-3" and inherit the badge's text colour.

To customise icon styling, use a cellRenderers override (React) or a #cell-{columnName} slot (Vue) - see Overriding Cells:

tsx
<InertiaTable
    tableData={servers}
    iconResolver={iconResolver}
    cellRenderers={{
        type: ({ value }) => {
            const Icon = iconResolver(String(value));
            return Icon ? <Icon className="h-6 w-6 text-blue-500" /> : null;
        },
    }}
/>
vue
<script setup lang="ts">
import { InertiaTable } from 'inertia-table-vue';
import { iconResolver } from '@/lib/icons';

defineProps<{ servers: any }>();
</script>

<template>
    <InertiaTable
        :table-data="servers"
        :icon-resolver="iconResolver"
    >
        <template #cell-type="{ value }">
            <component
                :is="iconResolver(String(value))"
                v-if="iconResolver(String(value))"
                class="h-6 w-6 text-blue-500"
            />
        </template>
    </InertiaTable>
</template>