Skip to main content
SocialLinksField ← Back to Table of Contents

Summary

Social profile link editor with a platform picker, one URL row per platform, per-platform hostname validation, optional custom platforms, row reordering, and URL auto-formatting on blur. Client-side validation mirrors server rules and blocks form submit when rows are invalid (same pattern as ScheduleField).
ClassBjanczak\FilamentFlexFields\Filament\Forms\Components\SocialLinksField
State typelist<array<platform: string, url: string>> (also accepts legacy associative map)
Model cast'social_links' => 'array' or 'json'
FieldType(no dedicated FieldType mapping yet — use the class directly)
Playgroundsocial-links-field slug in Flex Fields playground

Basic usage

use Bjanczak\FilamentFlexFields\Filament\Forms\Components\SocialLinksField;

SocialLinksField::make('social_links')
    ->label('Social profiles')
    ->required();
Filament resource example:
use Filament\Forms\Form;
use Bjanczak\FilamentFlexFields\Filament\Forms\Components\SocialLinksField;

public static function form(Form $form): Form
{
    return $form->schema([
        SocialLinksField::make('social_links')
            ->label('Broker social links')
            ->helperText('Add each platform once, then paste the profile URL.')
            ->columnSpanFull(),
    ]);
}

State format

Each link is a list item with platform and url keys. Platform values match built-in enum slugs (instagram, x, linkedin, …) or custom platform value strings.

Minimal example

[
  { "platform": "instagram", "url": "https://instagram.com/laravelphp" },
  { "platform": "linkedin", "url": "https://linkedin.com/company/laravel" }
]

In-progress row (empty URL kept for validation)

While editing, rows with a selected platform but empty URL remain in Livewire state so server and client validation can report required errors:
[
  { "platform": "instagram", "url": "" },
  { "platform": "website", "url": "https://example.com" }
]
On successful save (dehydrate), rows with empty URLs are stripped — only complete links persist.

Legacy associative map (enum platforms only)

[
    'instagram' => 'https://instagram.com/laravelphp',
    'website' => 'https://example.com',
]
This shape is normalized to the list format. Empty URL values are kept during hydration (not dropped).

Default platforms

When platforms() is not set, all built-in platforms from SocialPlatform::defaults() are available: instagram, x, linkedin, youtube, facebook, tiktok, github, telegram, whatsapp, pinterest, threads, discord, messenger, reddit, twitch, vimeo, vk, website Use excludePlatforms() to remove entries from this default set without building a full whitelist.

Configuration API

All methods accept Closure for dynamic configuration.

platforms(array|Closure|null $platforms)

Whitelist which platforms appear in the picker. Accepts SocialPlatform enum cases and/or string values (including custom platform values defined via customPlatforms()). Default: null (use defaults minus exclusions, plus custom platforms).
use Bjanczak\FilamentFlexFields\Data\SocialPlatform;

SocialLinksField::make('footer_links')
    ->platforms([
        SocialPlatform::Instagram,
        SocialPlatform::X,
        SocialPlatform::LinkedIn,
        SocialPlatform::Website,
    ]);
Whitelist with a custom platform:
SocialLinksField::make('fediverse')
    ->platforms(['mastodon', SocialPlatform::Website])
    ->customPlatforms([
        [
            'value' => 'mastodon',
            'label' => 'Mastodon',
            'placeholder' => 'https://mastodon.social/@username',
            'hosts' => ['mastodon.social', 'mastodon.online'],
        ],
    ]);

excludePlatforms(array|Closure $platforms)

Exclude platforms from the default set when platforms() is not configured. Default: [].
SocialLinksField::make('social_links')
    ->excludePlatforms([
        SocialPlatform::Vk,
        SocialPlatform::Twitch,
        SocialPlatform::Reddit,
    ]);

customPlatforms(array|Closure $platforms)

Add or override platform metadata. Each entry is an array (or resolved from a Closure) with:
KeyRequiredDescription
valueyesUnique platform slug stored in state
labelnoDisplay label (defaults to value)
placeholdernoURL input placeholder
hostsnoAllowed URL host patterns; empty = any valid http(s) URL
iconSvgnoRaw SVG HTML for brand icon; falls back to link icon partial
Definitions are represented server-side by SocialPlatformDefinition.
SocialLinksField::make('social_links')
    ->customPlatforms([
        [
            'value' => 'bluesky',
            'label' => 'Bluesky',
            'placeholder' => 'https://bsky.app/profile/username',
            'hosts' => ['bsky.app', 'bsky.social'],
            'iconSvg' => '<svg class="fff-social-links__brand-icon" aria-hidden="true">...</svg>',
        ],
    ]);
Custom platforms are merged into the default picker list. When platforms() is set, only matching custom entries are included. Cap how many platform rows can be added. When reached, the picker shows a “all platforms added” message. Default: null (unlimited, still one row per platform).
SocialLinksField::make('social_links')
    ->maxLinks(5);

reorderable(bool|Closure $enabled = true)

Show up/down chevron buttons on each row to change display order. Default: false. Enable explicitly:
SocialLinksField::make('social_links')
    ->reorderable();
Disable again (e.g. in a closure):
SocialLinksField::make('social_links')
    ->reorderable(false);

autoFormatUrls(bool|Closure $enabled = true)

On URL input blur: trim whitespace and prepend https:// when no scheme is present. Default: true.
SocialLinksField::make('social_links')
    ->autoFormatUrls(false);

variant(string|Closure $variant)

Visual variant passed to shared flex text input tokens. Default: primary. Allowed: primary, secondary, soft, flat, ghost.
SocialLinksField::make('social_links')
    ->variant('soft');

size(string|Closure $size)

Control density via HasControlSize. Default: md.
SocialLinksField::make('social_links')
    ->size('sm');

readOnly() / disabled()

Standard Filament interaction states — picker, add, remove, reorder, and URL inputs become non-interactive.

required()

Field is empty when no rows have a non-empty URL after normalization. Rows with platform selected but empty URL fail validation.

focusOutline(bool|Closure $enabled = true)

Show focus ring on URL input shells (HasFieldFocusOutline).

Public helper methods

MethodReturnsDescription
getPlatformValues()list&lt;string&gt;Resolved platform slugs
getPlatformDefinitionMap()array&lt;string, SocialPlatformDefinition&gt;Full metadata map
getPlatformDefinitions()list&lt;array&gt;Alpine-ready definitions including hosts
getBrandIconSvgs()array&lt;string, string&gt;Rendered SVG map per platform
getAlpineConfiguration()arrayFull JS config blob
getWrapperClasses()array&lt;string, bool&gt;BEM wrapper class map
isReorderable()boolWhether reorder UI is enabled
shouldAutoFormatUrls()boolWhether blur formatting runs
getMaxLinks()?intLink cap
getVariant() / getSize()stringResolved presentation tokens

Validation

Server (SocialLinkValidator)

RuleMessage key
Empty platform or URLsocial_links.validation.required
Unknown platform (not in definition map)social_links.validation.unknown_platform
Platform not in whitelistsocial_links.validation.platform_not_allowed
Invalid URL / non-http(s) schemesocial_links.validation.invalid_url
Host does not match platform hostssocial_links.validation.platform_mismatch
Row errors are wrapped with social_links.validation.row (:platform: :message). Built-in host rules mirror SocialPlatform::hostPatterns(). Custom platforms use their hosts array. website and custom entries with empty hosts accept any valid http(s) URL. Same rules using dynamic hosts from getPlatformDefinitions(). On form submit (capture phase):
  1. showValidationErrors = true
  2. validateAllRows()
  3. If errors exist: preventDefault(), stopImmediatePropagation(), sync first error to Livewire via $wire.addError(statePath, message)
This matches the ScheduleField submit guard pattern.

Recipes

SocialLinksField::make('footer_socials')
    ->label('Footer social icons')
    ->platforms([
        SocialPlatform::Instagram,
        SocialPlatform::X,
        SocialPlatform::LinkedIn,
        SocialPlatform::YouTube,
        SocialPlatform::Website,
    ])
    ->maxLinks(4)
    ->variant('secondary')
    ->size('sm');

Broker profile — required with auto-format

SocialLinksField::make('broker_socials')
    ->label('Public profiles')
    ->required()
    ->autoFormatUrls()
    ->reorderable();

Exclude niche platforms from defaults

SocialLinksField::make('social_links')
    ->excludePlatforms([
        SocialPlatform::Vk,
        SocialPlatform::Twitch,
        SocialPlatform::Messenger,
    ]);

Fediverse / custom brand

SocialLinksField::make('social_links')
    ->platforms(['mastodon', 'bluesky', SocialPlatform::Website])
    ->customPlatforms([
        [
            'value' => 'mastodon',
            'label' => 'Mastodon',
            'placeholder' => 'https://mastodon.social/@username',
            'hosts' => ['mastodon.social', 'mastodon.online'],
        ],
        [
            'value' => 'bluesky',
            'label' => 'Bluesky',
            'placeholder' => 'https://bsky.app/profile/handle',
            'hosts' => ['bsky.app', 'bsky.social'],
        ],
    ]);

Read-only display on view page

SocialLinksField::make('social_links')
    ->readOnly()
    ->default(fn ($record) => $record->social_links);

Dynamic platform list from tenant settings

SocialLinksField::make('social_links')
    ->platforms(fn (): array => tenant()->allowedSocialPlatforms())
    ->maxLinks(fn (): int => tenant()->socialLinkLimit());

Eloquent model persistence

// Model
protected $casts = [
    'social_links' => 'array',
];

// Saving — dehydrated state is null or list of complete links
$broker->social_links = $data['social_links'];

UI behaviour

FeatureDetail
Platform pickerTeleported dropdown listbox; one platform per row maximum
Add flowSelect platform → Add → focus URL input
URL inputFlexTextInput shell; per-row inline errors
ReorderUp/down circle buttons when reorderable(true)
Auto-formatBlur trims and adds https:// when autoFormatUrls(true)
Picker a11yArrowUp/ArrowDown/Home/End/Enter/Escape; aria-activedescendant; .is-highlighted option
SSR + hydrationServer-rendered list for first paint; Alpine live list after is-hydrated
State syncwire:ignore + $entangle — full links JSON synced to Livewire
Submit guardBlocks native submit when client validation fails; syncs first error to Livewire

CSS classes

ClassRole
fff-social-linksRoot wrapper
fff-social-links-fieldField wrapper (via getWrapperClasses)
fff-social-links-field--{sm|md|lg}Size modifier
fff-social-links-field--{variant}Variant modifier
fff-social-links-field--reorderableReorder feature enabled
fff-social-links__toolbarPicker + Add button row
fff-social-links__add-triggerPlatform dropdown trigger
fff-social-links__add-btnConfirm add button
fff-social-links__platform-menuTeleported listbox panel
fff-social-links__platform-optionPicker option
fff-social-links__platform-option.is-highlightedKeyboard-highlighted option
fff-social-links__platform-option.is-activeCurrently selected platform
fff-social-links__listLink rows container
fff-social-links__itemSingle platform row
fff-social-links__item-actionsReorder button group
fff-social-links__reorder-btnUp/down circle button
fff-social-links__remove-btnRemove row button
fff-social-links__row-errorInline validation message
fff-social-links__brand-iconSVG icon wrapper in partial

Assets

AssetPurpose
Stylesheet social-links-fieldComponent layout and states
Alpine social-links-fieldInteractive field logic
Dependency flex-text-inputURL input shell
Dependency teleported-menuPlatform dropdown positioning
Registered in FlexFieldAssets. Blade includes load-stylesheet partial and x-load Alpine component.

PHP support classes

ClassRole
SocialPlatformBuilt-in platform enum, labels, placeholders, host patterns
SocialPlatformDefinitionDTO for built-in + custom platform metadata
SocialLinksNormalizerHydrate/normalize/dehydrate state shapes
SocialLinkValidatorServer-side URL + host validation

JavaScript modules

ModuleRole
resources/js/components/social-links-field.jsAlpine component, submit guard, reorder, picker a11y
resources/js/support/social-link-validation.jsShared validation, normalization, URL formatting helpers
Exported test helpers: collectSocialLinkRowErrors, hasSocialLinkValidationErrors, firstSocialLinkValidationError, dehydrateSocialLinksState, formatSocialLinkUrl.

Testing

# PHP unit + feature
php artisan test --compact tests/Unit/SocialLinksFieldTest.php
php artisan test --compact tests/Feature/SocialLinksFieldRenderTest.php

# JS helpers
node --test tests/js/social-link-validation.test.mjs
node --test tests/js/social-links-field.test.mjs

# Playground E2E (requires FLEX_FIELDS_PLAYGROUND_URL)
npx playwright test tests/e2e/playground-social-links-field.spec.mjs

  • LinkPreviewField — fetch Open Graph preview for a single URL
  • PhoneField — similar teleported picker + validation patterns
  • ScheduleField — submit guard reference implementation