Skip to content

Always abort fold when @aware prop is dynamic, regardless of safe list#181

Closed
joshhanley wants to merge 2 commits into
mainfrom
josh/abort-fold-for-dynamic-aware-props-in-safe-list
Closed

Always abort fold when @aware prop is dynamic, regardless of safe list#181
joshhanley wants to merge 2 commits into
mainfrom
josh/abort-fold-for-dynamic-aware-props-in-safe-list

Conversation

@joshhanley
Copy link
Copy Markdown
Member

@joshhanley joshhanley commented May 18, 2026

The Scenario

A Flux user wraps <flux:select> in an anonymous component that forwards a dynamic :indicator. They expect each <flux:select.option> to render a checkbox indicator when multiple is true. Instead, each option renders the default indicator. Replacing the dynamic value with a static indicator="checkbox" works, so the bug only happens when :indicator is supplied dynamically by the parent.

Screenshot 2026-05-18 at 01 10 19PM@2x
<flux:select
    variant="listbox"
    :multiple="$multiple"
    :indicator="$multiple ? 'checkbox' : null"
>
    @foreach($items as $item)
        <flux:select.option :value="$item->id">{{ $item->name }}</flux:select.option>
    @endforeach
</flux:select>

The Problem

Flux's select/option/index.blade.php lists indicator in both @aware and safe::

@blaze(fold: true, safe: ['filterable', 'indicator', 'loading'])
@aware(['variant', 'indicator'])

v1.0.12 added a guard so that an @aware prop supplied dynamically by the parent aborts the fold. But the guard runs the prop through the same $unsafe lookup that safe: subtracts from, so any prop listed in both is silently re-permitted and the fold proceeds. The @aware runtime lookup is then compiled away and the child renders with the prop's default.

The Solution

Abort the fold immediately when an @aware prop is supplied dynamically by the parent, without consulting safe:. A safe: declaration is a promise about dynamic values passed on the component itself; it cannot apply to values that enter via @aware, because folding eliminates the @aware lookup entirely.

Screenshot 2026-05-18 at 01 13 08PM@2x

Fixes livewire/flux#2613

When an `@aware` prop is supplied dynamically by the parent, the fold
must be aborted so the runtime `@aware` lookup survives. v1.0.12 added
this guard, but routed the prop through the same `$unsafe` lookup that
`safe:` subtracts from, so any prop listed in both was silently
re-permitted and the fold proceeded.

Short-circuit the aware-from-parent-dynamic case before it touches
`safe:`/`$unsafe`. A `safe:` declaration is a promise about dynamic
values passed on the component itself; it cannot apply to values that
enter via `@aware`, because folding eliminates the `@aware` lookup
entirely.

Fixes livewire/flux#2613
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 18, 2026

Benchmark Result: Default

Attempt Blade Blaze Improvement
#1 357.85ms 15.89ms 95.6%
#2 350.32ms 15.81ms 95.5%
#3 356.09ms 15.86ms 95.5%
#4 361.75ms 15.96ms 95.6%
#5 352.74ms 15.83ms 95.5%
#6 359.00ms 15.74ms 95.6%
#7 * 354.82ms 16.43ms 95.4%
#8 355.19ms 15.78ms 95.6%
#9 349.94ms 15.94ms 95.4%
#10 353.80ms 15.76ms 95.5%
Snapshot 355.63ms 15.80ms 95.6%
Result 355.19ms (~) 15.83ms (~) 95.5% (~)

Median of 10 attempts (* = outlier, excluded from result), 5000 iterations x 10 rounds, 46.67s total

To run a specific benchmark, comment /benchmark <name> where name is one of: attributes, aware, class, default, forwarding, merge, named-slots, no-attributes, slot

@ganyicz
Copy link
Copy Markdown
Collaborator

ganyicz commented May 20, 2026

The real problem here is that indicator is marked as safe.

In this example from the test case it would be incorrect to mark type as safe, whether it comes from aware or directly because it is not a passthrough prop, it changes the layout.

@blaze(fold: true, safe: ['type'])
@aware(['type' => 'text’])

@if ($type == 'number')
    <input type="number">
@else
    <input type="text">
@endif

Looking at the blaze directive in select/option/index.blade.php, the props listed in safe should in fact be unsafe.

I’ll open a PR in flux.

@ganyicz ganyicz closed this May 20, 2026
@joshhanley joshhanley deleted the josh/abort-fold-for-dynamic-aware-props-in-safe-list branch May 20, 2026 22:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

flux:select.option ignores dynamic :indicator from parent due to safe: ['indicator'] bypassing Blaze v1.0.12 dynamic-aware abort

2 participants