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 '@forjedio/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 '@forjedio/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 '@forjedio/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 '@forjedio/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 '@forjedio/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 '@forjedio/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 '@forjedio/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 '@forjedio/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 '@forjedio/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>