
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 asScheduleField).
| Class | Bjanczak\FilamentFlexFields\Filament\Forms\Components\SocialLinksField |
| State type | list<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) |
| Playground | social-links-field slug in Flex Fields playground |
Basic usage
State format
Each link is a list item withplatform and url keys. Platform values match built-in enum slugs (instagram, x, linkedin, …) or custom platform value strings.
Minimal example
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:dehydrate), rows with empty URLs are stripped — only complete links persist.
Legacy associative map (enum platforms only)
Default platforms
Whenplatforms() 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 acceptClosure 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).
excludePlatforms(array|Closure $platforms)
Exclude platforms from the default set when platforms() is not configured.
Default: [].
customPlatforms(array|Closure $platforms)
Add or override platform metadata. Each entry is an array (or resolved from a Closure) with:
| Key | Required | Description |
|---|---|---|
value | yes | Unique platform slug stored in state |
label | no | Display label (defaults to value) |
placeholder | no | URL input placeholder |
hosts | no | Allowed URL host patterns; empty = any valid http(s) URL |
iconSvg | no | Raw SVG HTML for brand icon; falls back to link icon partial |
SocialPlatformDefinition.
platforms() is set, only matching custom entries are included.
maxLinks(int|Closure|null $max)
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).
reorderable(bool|Closure $enabled = true)
Show up/down chevron buttons on each row to change display order.
Default: false. Enable explicitly:
autoFormatUrls(bool|Closure $enabled = true)
On URL input blur: trim whitespace and prepend https:// when no scheme is present.
Default: true.
variant(string|Closure $variant)
Visual variant passed to shared flex text input tokens.
Default: primary. Allowed: primary, secondary, soft, flat, ghost.
size(string|Closure $size)
Control density via HasControlSize.
Default: md.
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
| Method | Returns | Description |
|---|---|---|
getPlatformValues() | list<string> | Resolved platform slugs |
getPlatformDefinitionMap() | array<string, SocialPlatformDefinition> | Full metadata map |
getPlatformDefinitions() | list<array> | Alpine-ready definitions including hosts |
getBrandIconSvgs() | array<string, string> | Rendered SVG map per platform |
getAlpineConfiguration() | array | Full JS config blob |
getWrapperClasses() | array<string, bool> | BEM wrapper class map |
isReorderable() | bool | Whether reorder UI is enabled |
shouldAutoFormatUrls() | bool | Whether blur formatting runs |
getMaxLinks() | ?int | Link cap |
getVariant() / getSize() | string | Resolved presentation tokens |
Validation
Server (SocialLinkValidator)
| Rule | Message key |
|---|---|
| Empty platform or URL | social_links.validation.required |
| Unknown platform (not in definition map) | social_links.validation.unknown_platform |
| Platform not in whitelist | social_links.validation.platform_not_allowed |
| Invalid URL / non-http(s) scheme | social_links.validation.invalid_url |
Host does not match platform hosts | social_links.validation.platform_mismatch |
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.
Client (Alpine + social-link-validation.js)
Same rules using dynamic hosts from getPlatformDefinitions(). On form submit (capture phase):
showValidationErrors = truevalidateAllRows()- If errors exist:
preventDefault(),stopImmediatePropagation(), sync first error to Livewire via$wire.addError(statePath, message)
ScheduleField submit guard pattern.
Recipes
CMS footer — small whitelist + cap
Broker profile — required with auto-format
Exclude niche platforms from defaults
Fediverse / custom brand
Read-only display on view page
Dynamic platform list from tenant settings
Eloquent model persistence
UI behaviour
| Feature | Detail |
|---|---|
| Platform picker | Teleported dropdown listbox; one platform per row maximum |
| Add flow | Select platform → Add → focus URL input |
| URL input | FlexTextInput shell; per-row inline errors |
| Reorder | Up/down circle buttons when reorderable(true) |
| Auto-format | Blur trims and adds https:// when autoFormatUrls(true) |
| Picker a11y | ArrowUp/ArrowDown/Home/End/Enter/Escape; aria-activedescendant; .is-highlighted option |
| SSR + hydration | Server-rendered list for first paint; Alpine live list after is-hydrated |
| State sync | wire:ignore + $entangle — full links JSON synced to Livewire |
| Submit guard | Blocks native submit when client validation fails; syncs first error to Livewire |
CSS classes
| Class | Role |
|---|---|
fff-social-links | Root wrapper |
fff-social-links-field | Field wrapper (via getWrapperClasses) |
fff-social-links-field--{sm|md|lg} | Size modifier |
fff-social-links-field--{variant} | Variant modifier |
fff-social-links-field--reorderable | Reorder feature enabled |
fff-social-links__toolbar | Picker + Add button row |
fff-social-links__add-trigger | Platform dropdown trigger |
fff-social-links__add-btn | Confirm add button |
fff-social-links__platform-menu | Teleported listbox panel |
fff-social-links__platform-option | Picker option |
fff-social-links__platform-option.is-highlighted | Keyboard-highlighted option |
fff-social-links__platform-option.is-active | Currently selected platform |
fff-social-links__list | Link rows container |
fff-social-links__item | Single platform row |
fff-social-links__item-actions | Reorder button group |
fff-social-links__reorder-btn | Up/down circle button |
fff-social-links__remove-btn | Remove row button |
fff-social-links__row-error | Inline validation message |
fff-social-links__brand-icon | SVG icon wrapper in partial |
Assets
| Asset | Purpose |
|---|---|
Stylesheet social-links-field | Component layout and states |
Alpine social-links-field | Interactive field logic |
Dependency flex-text-input | URL input shell |
Dependency teleported-menu | Platform dropdown positioning |
FlexFieldAssets. Blade includes load-stylesheet partial and x-load Alpine component.
PHP support classes
| Class | Role |
|---|---|
SocialPlatform | Built-in platform enum, labels, placeholders, host patterns |
SocialPlatformDefinition | DTO for built-in + custom platform metadata |
SocialLinksNormalizer | Hydrate/normalize/dehydrate state shapes |
SocialLinkValidator | Server-side URL + host validation |
JavaScript modules
| Module | Role |
|---|---|
resources/js/components/social-links-field.js | Alpine component, submit guard, reorder, picker a11y |
resources/js/support/social-link-validation.js | Shared validation, normalization, URL formatting helpers |
collectSocialLinkRowErrors, hasSocialLinkValidationErrors, firstSocialLinkValidationError, dehydrateSocialLinksState, formatSocialLinkUrl.
Testing
Related components
- LinkPreviewField — fetch Open Graph preview for a single URL
- PhoneField — similar teleported picker + validation patterns
- ScheduleField — submit guard reference implementation