Brief Introduction
Here in this article, we will design some basic laravel form components which will give you all the aspects of laravel components as well as dynamic attributes so you have an idea about it with good laravel component examples.
What is a component?
A component is an independent, reusable and isolated single UI block. Components are always a great choice to divide the large UI into small chunks. Dynamic attributes are the main hero of any component. DRY(Don’t Repeat Yourself) with less code complexity is the main advantage to use components. So always design a component in a scaler way because we will reuse it in multiple ways.
Laravel Components are as same as sections, layouts, and includes but it is easier to understand and derive with more functionalities and flexibilities. Where we can easily modify with the dynamic attributes. There are two types of components, class-based components and anonymous components. In form components, we will mainly use anonymous components.
The Anonymous component is the component that has only a blade template with no class. Here is a command to create the anonymous component,
php artisan make:component <component-name> --view
As an example, If we want to create the component for the navbar then we have to fire,
php artisan make:component navbar --view
So it will create the blade template under the /resources/views/components
folder. I hope you will get the basics now let’s dive into the form components.
Creating Laravel Application
We will start by creating a new Laravel application if you have already then you can skip this section. Open your command line terminal and run the below command to create a Laravel project using composer.
composer create project laravel/laravel form-components
Installing Laravel Breeze
We will use the default Laravel Breez to install the authentication features. This will give us a default environment for login, registration, password reset, email verification, and password confirmation. Laravel Breeze’s default view layer is made up of simple Blade templates styled with Tailwind CSS. Here are the necessary commands to start with Laravel Breeze,
composer require laravel/breeze --dev
php artisan breeze:install
Migrate Database
Add your database configurations in the .env
file and fire the migration command to migrate the database,
php artisan migrate
Creating Form Components
We will create the form component in the respective forms directory. So the path will be /resources/views/components/forms
. When you fire the command it will create a new component in the forms directory.
The dot is always used to create the nested directories so if you want two nested directories then the command will be directory1.directory2.component
and it will create the component as /resources/views/components/directory1/directory2/component.blade.php
.
We will render the component as <x-directory.component />
. Where the directory is optional if there is no directory then it will be as <x-component />
. The component tag starts from x-
and is followed by the kebab case of the component name. As I mentioned earlier the dynamic attributes are the hero of any component. So here to define dynamic attributes we will use the colon(:) syntax. Everything starts with a colon considered as the dynamic attribute. In the dynamic attribute, you can directly write the laravel code because it is kind of a variable.
The HTML attributes are default available in the component as variables. So if you pass the label then we have $label
defaults in a component.
Here the catch is the attribute bag, it will contain all the HTML attributes in the $attributes
array as well as the extra attributes which you want to pass. The important thing is you can merge the additional values with the existing attribute values. Here is a good example of that,
Alert Component
<div {{ $attributes->merge(["class" => "p-4 font-bold text-lg alert"]) }}>
{{ $message }}
</div>
You can now merge the additional classes according to the conditions,
<x-alert class="alert-success" :message="$message" />
<x-alert class="alert-danger" :message="$message" />
<x-alert class="alert-warning" :message="$message" />
In this way, you can merge any attributes with dynamic values as well as you can define new attributes on the top of components with the help of the @props
directive. I am not a fan of @prop
because if we want to use it then we have to define all the dynamic attributes in props. It will not merge the new @props attributes with the attribute bag. But obviously, we need to use it in some conditional logic.
Here is a good example of a @prop
directive,
@props([
"alertType" => "alert-primary",
"message"
])
<div {{ $attributes->merge(["class" => "p-4 font-bold text-lg alert ".$alertType]) }}>
{{ $message }}
</div>
But as I mention it will not default merge to the attributes bag so we also have to at least define the message with alertType too.
Now I think we are good to go to explore the different types of components. Here are some Anonymous Form Components which are based on tailwind CSS. You can just download and use it in your way.
Text Field Component
php artisan make:component forms.text-field --view
<div>
<label
for="{{ $name }}"
class="{{ ($required ?? false) ? 'after:content-["*"]' : '' }} block font-medium text-sm text-gray-700">
{{ $label }}
</label>
<input
type="{{ $type ?? 'text' }}"
placeholder="{{ $placeholder ?? '' }}"
value="{{ old($name, $value ?? false) }}"
id="{{ $name }}"
{{ ($required ?? false) ? 'required' : '' }}
{{ $attributes->merge(['class' => 'block mt-1 w-full rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50']) }}
>
@error($name)
<p class="text-sm text-red-600 mt-1" role="alert">{{ $message }}</p>
@enderror
</div>
<x-forms.text-field
label="Full Name"
name="name"
placeholder="Please Enter Your Full Name"
:value="$user->name"
required
/>
Select Field Component
php artisan make:component forms.select-field --view
<div>
<label
for="{{ $name }}"
class="{{ ($required ?? false) ? 'after:content-["*"]' : '' }} block font-medium text-sm text-gray-700">
{{ $label }}
</label>
<select
name="{{ $name }}"
id="{{ $name }}"
class="block mt-1 w-full rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
>
@isset($placeholder)
<option value="">{{ $placeholder }}</option>
@endisset
@foreach ($options as $optionKey => $optionValue)
<option
value="{{ $optionKey }}"
@if(in_array($optionKey, $disableOptions)) disabled @endif
@if(old($name, $value ?? false) == $optionKey) selected @endif
>{{ $optionValue }}</option>
@endforeach
</select>
@error($name)
<p class="text-sm text-red-600 mt-1" role="alert">{{ $message }}</p>
@enderror
</div>
<x-forms.select-field
label="Country"
name="country"
placeholder="Please select country"
:options="[
'91' => 'India',
'44' => 'UK',
'1' => 'USA'
]"
:disableOptions="[
'44'
]"
:value="$user->country"
/>
Checkbox Component
php artisan make:component forms.checkbox --view
@php
$selectedOptions = old(str_replace('[]', '', $name), $value ?? false) ?? []
@endphp
<div>
<label
class="{{ ($required ?? false) ? 'after:content-["*"]' : '' }} block font-medium text-sm text-gray-700"
>{{ $label }}</label>
<div>
@foreach ($options as $option)
<input
type="checkbox"
name="{{ $name }}"
value="{{ $option['value'] }}"
@if(is_array($selectedOptions) && in_array($option['value'], $selectedOptions)) checked @endif
>
<label
class="font-medium text-sm text-gray-700 {{ ($option['class'] ?? false) ? $option['class'] : '' }}"
>{{ $option['label'] }}</label><br>
@endforeach
</div>
@error($name)
<p class="text-sm text-red-600 mt-1" role="alert">{{ $message }}</p>
@enderror
</div>
<x-forms.checkbox
label="Hobbies"
name="hobbies[]"
:options="[
['label' => 'Singing', 'value' => 'singing', 'class' => 'text-red-700'],
['label' => 'Travelling', 'value' => 'travelling'],
['label' => 'Reading', 'value' => 'reading'],
]"
:value="$user->hobbies"
/>
Radio Component
php artisan make:component forms.radio --view
@php
$checked = old($name, $value ?? null) == $optionValue;
@endphp
<input
type="radio"
id="{{ $optionValue }}"
name="{{ $name }}"
value="{{ $optionValue }}"
@if($checked) checked @endif
/>
<label
for="{{ $optionValue }}"
class="font-medium text-sm text-gray-700"
>{{ $label }}</label><br>
<x-forms.radio
label="Male"
name="gender"
:option-value="'male'"
:value="$user->gender"
/>
<x-forms.radio
label="Female"
name="gender"
:option-value="'female'"
:value="$user->gender"
/>
Textarea Component
php artisan make:component forms.textarea --view
<div>
<label
for="{{ $name }}"
class="{{ ($required ?? false) ? 'after:content-["*"]' : '' }} block font-medium text-sm text-gray-700">
{{ $label }}
</label>
<div>
<textarea
id="{{ $name }}"
name="{{ $name }}"
rows="10"
{{ $attributes->merge(['class' => 'block mt-1 w-full rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50']) }}
>{{ old($name, $value ?? false) }}</textarea>
</div>
@error($name)
<p class="text-sm text-red-600 mt-1" role="alert">{{ $message }}</p>
@enderror
</div>
<x-forms.textarea
label="About"
name="about"
:value="$user->about"
/>
Button Components
Form Button
<form
method="POST"
action="{{ $action }}"
>
@csrf
@method($method ?? 'POST')
<button
type="submit"
class="{{ $class ?? '' }}"
>
{{ $slot }}
</button>
</form>
Primary/Submit Button
<button
{{ $attributes->merge(['type' => 'submit', 'class' => 'btn btn-primary']) }}
>{{ $slot }}</button>
Cancel Button
<a
href="{{ $redirectTo ?? 'javascript:void(0)' }}"
{{ $attributes->merge(['class' => 'underline text-sm text-gray-600 hover:text-gray-900']) }}
>
{{ $label ?? __('Cancel') }}
</a>
Here in the checkbox component, I make it whole as a component where we have to pass options as an array. But instead, we can also make the single option component.
As vice versa for the radio. I make it as an individual option component instead to create the whole at once.
Do comment in the comment section and let me know what you think. Which option will be flexible, scalable and obviously with less code? Other options are also welcome.