Props vs attributes
Props and attributes are indistinguishable on the surface, however, we've found it helpful to make a distinction between flux-provided properties called "props" and bespoke "attributes" that will be forwarded directly to an underlying HTML element.
For example, the following component uses a prop called
variant
as well as a bespoke
x-on:change.prevent
attribute:
When the component is rendered in the browser, the output HTML might will look something like this:
As you can see, the
x-on:change.prevent
attribute was forwarded directly to the underlying HTML element, while the
variant
prop was instead internally used to customize which classes were applied to the input.
Class merging
Most flux components apply Tailwind classes to their underlying elements, however, you can also pass your own classes into components and they will be automatically merged with the classes applied by flux.
Here's an example of making a button full width by passing in the
w-full
Tailwind utility class:
Now, when the component is rendered, the output HTML will contain both the
w-full
class and other classes applied by flux:
Dealing with class merging conflicts
Occasionally, you may run into conflicts when passing in a Tailwind utility that flux also applies internally.
For example, you may try to customize the background color of a button by passing in the
bg-zinc-800
Tailwind utility class:
However, because flux applies it's own
bg-*
attributes, both classes will be rendered in the output HTML:
Because both classes exist on the element, the one defined last in CSS wins.
Tailwind's important (
!
) modifier is the simplest way to resolve these conflicts:
The conflicting utilities will still both be rendered, however the user-defined ones with the
!
modifier will take precedence.
Usage of the
!
modifier can cause more problems than it solves. Below are a few alternatives to consider instead:
-
Publishing the component and adding your own variant
-
Globally customizing the component by styling the data attributes
-
Writing a new component for your unique case
Split attribute forwarding
In a perfect world, each flux component would directly render a single HTML element. However, in practice, some components are more complex than that.
For example, the
flux:input
component actually renders two HTML elements by default. A wrapping
<div>
and an underlying
<input>
element:
This presents a problem when passing bespoke Tailwind classes into the component: which element should receive the provided classes?
In these cases flux will often split up the forwarded attributes into two groups: styling related ones like
class
and behavioral ones like
autofocus
. It will then apply each to wherever is most appropriate.
For example consider passing in the following
class
and
autofocus
attributes:
Flux will apply the
w-full
class to the wrapping
<div>
element and the
autofocus
attribute to the underlying
<input>
element:
Common props
Now that you've seen some of the nuances of attributes, let's look at some common prop patterns you will encounter as you use Flux.
Variant
First up is "variant". Any component that offers an alternate visual style uses the
variant
prop to do so.
Here's a list of a few common component variants:
If you find the need for a variant we don't offer, don't be afraid to
publish a component and add your own.
Icon
Rather than manually adding, styling, and spacing icons inside component slots, you can simply pass the
icon
prop to any component that supports it.
Flux uses
Heroicons for its icon collection. Heroicons is a set of beautiful and functional icons crafted by the fine folks at
Tailwind Labs.
Here's a handful of components you can pass icons into:
Occasionally, if a component supports adding an icon to the end of an element instead of the beginning by default, you can pass the
icon-trailing
prop as well:
Size
Some components offer size variations through the
size
prop.
Here are a few components that can be sized-down:
Here are a few that can be sized up for larger visual contexts:
Keyboard hints
Similar to the
icon
prop, many components allow you to add decorative hints for keyboard shortcuts using the
kbd
prop:
Inset
Certain components offer the
inset
prop which makes it easy to add the exact amount of negative margin needed to "inset" an element on any axis you specify.
This is extremely useful for putting a button or a badge inline with other text and not stretching the entire height of the container.
Prop forwarding
Occasionally, one component will wrap another and expose it as a simple prop.
For example, here is the Button component that allows you to set the icon using the simple
icon
prop instead of passing an entire Icon component into the Button as a child:
This is often a more desirable alternative to composing the entire component like so:
However, when using the
icon
prop, you are unable to add additional props to the Icon component like
variant
.
In these cases, flux will often expose nested props via prefixes like so:
Opt-out props
Sometimes flux will turn a prop "on" by default, or manage its state internally. For example, the
current
prop on the
navbar.item
component.
However, you may want to enforce that its value remains
false
for a specific instance of the component.
In these cases, you can use Laravel's dynamic prop syntax (
:
) and pass in
false
.
Shorthand props
Sometimes a component arrangement is both common and verbose enough that it warrants a shorthand syntax.
For example, here's a full input field:
This can be shortened to the following:
Internally, flux will expand the above into the full assembly of
field
,
label
,
input
, and
error
components.
This way you can keep syntax short and concise, but still have the ability to fully customize things if you'd like to use the full longform syntax.
You'll also encounter this pattern with tooltips and buttons.
Here's a long-form toolip:
Because the above is often repetitive, you can shorten it to a simple
tooltip
prop:
Data binding
In Livewire you are used to adding
wire:model
directly to input elements inside your forms.
In Flux, the experience is no different. Here are some common components you will often be adding
wire:model
to:
In addition to these common ones you'd expect, here are a few other components you can control via
wire:model
:
Of course, you can also pass
x-model
or
x-on:change
to any of these and they should behave exactly like native input elements.
Component Groups
When a Flux component can be "grouped", but is otherwise a stand-alone component, its wrapper component has a
.group
suffix.
All of the following components can be used on their own, or grouped together:
Alternatively, if a component can NOT be used on its own, but can be grouped, the wrapper will often be the main name of the component, and each child will have a
.item
suffix:
Root components
Most of the time, when a component can be composed of many sub-components, you will see compound names like the ones mentioned above.
However, for components that are larger or more "primitive" feeling than the others, they will lack a common prefix, and instead use the name of the component itself.
For example, this is flux's field component:
You'll notice bare component names like
flux:label
, instead of
flux:field.label
.
The reasoning for this is to avoid overly verbose heirarchies in common components that would result in naming like:
flux:field.label.badge
.
You will also see this pattern with
<flux:table>
.
Anomalies
As painful as it is, sometimes for something to "feel right", you have to abandon consistency for one reason or another.
For example, the
flux:tabs
component doesn't follow the aforementioned rules:
Slots
In general, Flux prefers composing multiple components together rather than using slots.
However, there are times where there is no substitute and slots are the perfect solution.
To demonstrate, consider the following input component with an
x-mark
icon:
If you wanted to wrap the icon in a clickable button to perform an action there is no way to achieve that without slots (or without offering an extremely verbose, custom syntax).
Here is the above, rewritten with a wrapping button:
Paper cuts
Here are a few gotchas that you might encounter while using Flux.
Blade components vs HTML elements
When dealing with plain HTML elements in Blade, you are free to use expressions like
@if
inside opening tags.
However, those expressions are not supported inside the opening tag of a Blade or Flux component.
Instead, you must stay within the confines of the Blade component
dynamic attribute syntax: