Skip to main content
Divide 100% between two to five options. Drag a divider to change its two neighboring weights together, or use the keyboard. Use TrafficSplit for A/B tests, campaign allocation, routing weights or a percentage-based budget.

One complete allocation

Neighboring weights change together. The total remains 100%.

Dynamic variants

Link segments to repeater rows and keep their count in sync.

First allocation

Add the field to your form schema:
The initial value is [34, 33, 33]. The default minimum is 12% per segment. Labels follow the array order; without custom labels, the field shows 1, 2, 3, and so on.

Segment count and initial weights

segmentCount() supports 2–5 segments. Direct PHP values outside this range are clamped to the nearest supported count. Supply exactly that many integer weights, each at least minWeight(), with a total of 100. default() applies to a new form; editing an existing record uses its saved allocation. Without default(), the field distributes weights equally. Any remaining percentage points go to the first segments: three options start at [34, 33, 33], and four at [25, 25, 25, 25].

Appearance

Variants

default uses bordered segments. secondary uses a filled surface. Both support the panel’s light and dark modes. These are the two supported variant names.

Three sizes

The default size is md. You can also pass ControlSize::Sm, ControlSize::Md or ControlSize::Lg from Bjanczak\FilamentFlexFields\Enums\ControlSize.

Custom labels

Missing or empty labels fall back to the segment number. Long labels are shortened to fit, with the full label available on hover. Numbers retain space within compact segments. Restore numbered labels with labels(null):

Percentage visibility

The percentage is displayed at 20% or above and hidden below it; the segment label remains. The default threshold is 18. It affects presentation only, not the saved value. The effective threshold is at least minWeight() + 1 and at most 100. On very narrow segments, the percentage also hides when there is insufficient room, so the segment number is not squeezed. The divider’s accessible description still includes both neighboring labels and weights.

Minimum weights and locked segments

Set the minimum

minWeight(5) prevents any weight from dropping below 5%. Whole percentage points are used throughout. Zero-weight segments are not supported; the lowest effective minimum is 1%.
Choose a feasible minimum: minWeight × segmentCount must not exceed 100. For five segments, the maximum feasible minimum is 20%. An impossible configuration raises an error.

Lock a weight in the interface

Indices start at zero: [1] locks the second segment. Both dividers touching it are disabled. In this example, C and D can still exchange weight. A divider cannot move if either neighboring segment is locked. Duplicate indices are ignored, and indices outside the segment count are discarded. Clear the locks with lockedSegments([]).
Locks control interaction. If a percentage must be fixed by your business rules, validate that requirement on the server too; a browser restriction is not an authorization rule.

Enforce a fixed percentage on the server

Disable the field

Disabled fields ignore pointer and keyboard changes. Filament does not normally save a disabled field’s state. Use ->dehydrated() only when you intentionally want that state included in the submitted data; server-side validation still applies. Use a repeater when the user can add and remove variants. Create the allocation once and attach its synchronization callback to the repeater:
  • Adding or removing a row distributes the weights equally across the new count.
  • Editing a row’s URL preserves the current allocation.
  • By default, the allocation is hidden below two rows and uses numbered labels.
  • Keep the repeater at a maximum of five rows. Linked allocations outside 2–5 rows fail validation when submitted.
  • Segment weights follow row order. Disable reordering, as above, when weights must retain their association with individual rows. If you enable reordering, manage the allocation-to-row association in your application.

Custom labels and visibility threshold

numericLabels: false retains your custom labels. minimumItems: 3 shows the field only when three or more rows exist; supported visibility thresholds are 2–5. This is a visibility setting, not a repeater minimum rule. Add minItems(3) to the repeater if three rows are mandatory. The state path is relative to the allocation’s container. If both fields are nested in a repeater row or group, use the path to the repeater from that same context. For a linked allocation, the row count determines the segment count, replacing a separately configured segmentCount().

Dynamic configuration

The component-specific count, minimum, threshold, variant, labels and locks accept closures. Size accepts a string, ControlSize or a closure. Closure arguments use Filament’s utility injection.
Make source fields live() so configuration changes reach the server and rerender the allocation. Changing the segment count recovers an allocation for the new count. Keep the minimum feasible for every possible count. linkedToRepeater() accepts a string or closure for its path; numericLabels and minimumItems are fixed arguments.

Validation and database storage

Built-in validation

Validation runs when the form is submitted. A valid allocation is an ordered list with:
  • exactly the configured number of weights;
  • integer percentages between minWeight() and 100;
  • a total of exactly 100;
  • a supported linked repeater count, when configured.
Decimals, negative values, booleans, nested arrays, keyed objects, malformed numeric strings and incorrect totals are rejected. Integer strings such as '25' are converted to integers.
Use required() when an allocation is mandatory. Standard Filament rules, labels, helper text and state-binding options are available. Add business validation with rules() as shown above.

Loading existing values

Initial hydration can recover an invalid saved allocation to an equal split or redistribute available weight across unlocked segments. That recovery is separate from submission validation: a forged payload is rejected rather than silently repaired during save.

Save to a JSON column

The field stores a list such as [50, 25, 25]. Use a JSON column and an Eloquent array cast:
Use the same name for the field and model attribute:
A normal Filament resource saves the validated state through its form workflow. In a custom Livewire form, obtain the validated data with $this->form->getState() before saving it. Do not persist unvalidated public properties directly.

Keyboard, touch and change events

Touch and pen use the same dividers as the mouse. Track measurements adjust to scroll and resize during a gesture. Locked and disabled dividers are skipped by keyboard focus. The view updates while dragging, and one complete allocation is committed when the gesture ends. Keyboard changes commit immediately. Deferred binding waits for the form’s next request; live() requests a server update after a committed change, rather than after every pointer movement.
The component also emits bubbling browser input and change events with event.detail.weights after a committed change:
For a linked repeater, linkedToRepeater() defaults to deferred allocation binding. Call live() after it if you explicitly need a server callback on each completed allocation change.

Configuration reference

Inherited form options include default(), required(), rules(), disabled(), label(), helperText(), columnSpanFull(), live() and afterStateUpdated(). These follow the normal Filament form API.

FLEX schema configuration

Use traffic_split as the schema type. traffic-split is the catalog route and asset identifier, not the saved schema type.
All eleven config keys map to the PHP options above. linked_repeater is a relative repeater path; leave it null for a standalone field. Set up the repeater callback and partial rendering in the containing form, as in the repeater example. Labels, defaults and required status are field-level properties outside config.

FlexSlider

One value or a pair of bounds on a numeric scale.

TrackSlider

A single numeric value on a compact track.

ProgressBar

Display a percentage without editing it.
Last modified on October 16, 2026