resources/views/layouts/main.blade.php
. Add the following code to the newly created file:<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Laravel 7 Blade Components</title> <link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet"> </head> <body class="h-screen w-screen flex items-center justify-center"> @yield('content') </body> </html>
routes/web.php
to point to a view that houses the profile form. Later we will add some data inline to simulate pulling data from a database and sending to our form.Route::get('profile', function(){ return view('profile'); });
profile.blade.php
in the resources/views
, and add the following code:@extends('layouts.main') @section('content') <form> <div> <div> <div> <h3 class="text-lg leading-6 font-medium text-gray-900"> Personal Information </h3> </div> <div class="mt-6 sm:mt-5"> <div class="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5"> <label for="first_name" class="block text-sm font-medium leading-5 text-gray-700 sm:mt-px sm:pt-2"> First name </label> <div class="mt-1 sm:mt-0 sm:col-span-2"> <div class="rounded-md shadow-sm"> <input id="first_name" class="bg-white border border-gray-200 py-2 px-3 rounded-lg w-full block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5"/> </div> </div> </div> <div class="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5"> <label for="last_name" class="block text-sm font-medium leading-5 text-gray-700 sm:mt-px sm:pt-2"> Last name </label> <div class="mt-1 sm:mt-0 sm:col-span-2"> <div class="rounded-md shadow-sm"> <input id="las_name" class="bg-white border border-gray-200 py-2 px-3 rounded-lg w-full block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5"/> </div> </div> </div> <div class="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5"> <label for="email" class="block text-sm font-medium leading-5 text-gray-700 sm:mt-px sm:pt-2"> Email address </label> <div class="mt-1 sm:mt-0 sm:col-span-2"> <div class="mt-1 sm:mt-0 sm:col-span-2"> <div class="rounded-md shadow-sm"> <input id="email" type="email" class="bg-white border border-gray-200 py-2 px-3 rounded-lg w-full block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5"/> </div> </div> </div> </div> <div class="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5"> <label for="street_address" class="block text-sm font-medium leading-5 text-gray-700 sm:mt-px sm:pt-2"> Street address </label> <div class="mt-1 sm:mt-0 sm:col-span-2"> <div class="rounded-md shadow-sm"> <input id="street_address" class="bg-white border border-gray-200 py-2 px-3 rounded-lg w-full block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5"/> </div> </div> </div> <div class="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5"> <label for="city" class="block text-sm font-medium leading-5 text-gray-700 sm:mt-px sm:pt-2"> City </label> <div class="mt-1 sm:mt-0 sm:col-span-2"> <div class="rounded-md shadow-sm"> <input id="city" class="bg-white border border-gray-200 py-2 px-3 rounded-lg w-full block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5"/> </div> </div> </div> <div class="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5"> <label for="state" class="block text-sm font-medium leading-5 text-gray-700 sm:mt-px sm:pt-2"> State / Province </label> <div class="mt-1 sm:mt-0 sm:col-span-2"> <div class="rounded-md shadow-sm"> <input id="state" class="bg-white border border-gray-200 py-2 px-3 rounded-lg w-full block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5"/> </div> </div> </div> <div class="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5"> <label for="zip" class="block text-sm font-medium leading-5 text-gray-700 sm:mt-px sm:pt-2"> ZIP / Postal </label> <div class="mt-1 sm:mt-0 sm:col-span-2"> <div class="rounded-md shadow-sm"> <input id="zip" class="bg-white border border-gray-200 py-2 px-3 rounded-lg w-full block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5"/> </div> </div> </div> </div> </div> </div> <div class="mt-8 border-t border-gray-200 pt-5"> <div class="flex justify-end"> <span class="ml-3 inline-flex rounded-md shadow-sm"> <button type="submit" class="inline-flex justify-center py-2 px-4 border border-transparent text-sm leading-5 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition duration-150 ease-in-out"> Save </button> </span> </div> </div> </form> @endsection
php artisan serve
. Navigate to http://127.0.0.1:8000/profile and you should see the following yield:<x-*>
tags to render dynamic content within a blade. These components differ from inline components in that they provide a mechanism for managing a component via a single file by utilizing a single view file, and have no associated class.” To add, they are super easy to create and use.components
directory in the resources/views
folder. Then create a subdirectory called inputs
. Our first component will be for our text inputs, so create a text.blade.php
file here and paste in the following code:<div class="rounded-md shadow-sm"> <input id="first_name" class="bg-white border border-gray-200 py-2 px-3 rounded-lg w-full block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5"/> </div>
<x-inputs.text />
! However, you’ll notice that our id
is hard-coded and this won’t properly support every field defined in the form above.@props
function that takes an array of items that you want to pass into the component. The component now looks like this:@props([ 'for' ]) <div class="rounded-md shadow-sm"> <input id="{{ $for }}" class="bg-white border border-gray-200 py-2 px-3 rounded-lg w-full block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5"/> </div>
id
we mentioned early can be passed into the generated form. To pass the property into the form, simply add it as an attribute when declaring the component:<x-inputs.text for="first_name"/>
<input id="first_name" class="bg-white border border-gray-200 py-2 px-3 rounded-lg w-full block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5"/>
with <x-inputs.text for="first_name"/>
@props([ 'for', 'type' => 'text' ])
name
, placeholder
, or additional classes to override any defaults. This is where $attributes
come in handy. All we have to do is add them when declaring the component as follows:<x-inputs.text for="first_name" placeholder="First name" name="name"/>
@props([ 'for', 'type' => 'text' ]) <div class="rounded-md shadow-sm"> <input type="{{ $type }}" {{ $attributes->merge(['class' => 'bg-white border border-gray-200 py-2 px-3 rounded-lg w-full block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5']) }} id="{{ $for }}" /> </div>
$attributes
directly onto the input and simultaneously allowing ourselves the flexibility to add classes that will be merged with our default classes. We can also use it like so:<x-inputs.text for="first_name" placeholder="First name" name="name" class="bg-red-600 text-white placeholder-white"/>
$slot
to render our inputs inside.resources/views/components/inputs
called group.blade.php
and add the following content:<div class="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5"> <label for="first_name" class="block text-sm font-medium leading-5 text-gray-700 sm:mt-px sm:pt-2"> First name </label> <div class="mt-1 sm:mt-0 sm:col-span-2"> {{ $slot }} </div> </div>
$slot
allows us to add whatever content we want in our component, in the specific place that we are yielding it. Back in our form we can replace the entire divs surrounding our input components like so:<x-inputs.group> <x-inputs.text for="first_name" placeholder="First name" name="name"/> </x-inputs.group>
@props(['label', 'for']) <div class="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5"> <label for="{{ $for }}" class="block text-sm font-medium leading-5 text-gray-700 sm:mt-px sm:pt-2"> {{ $label }} </label> <div class="mt-1 sm:mt-0 sm:col-span-2"> {{ $slot }} </div> </div>
@extends('layouts.main') @section('content') <form> <div> <div> <div> <h3 class="text-lg leading-6 font-medium text-gray-900"> Personal Information </h3> </div> <div class="space-y-6 sm:mt-5"> <x-inputs.group label="First Name" for="first_name"> <x-inputs.text for="first_name"/> </x-inputs.group> <x-inputs.group label="Last Name" for="last_name"> <x-inputs.text for="last_name"/> </x-inputs.group> <x-inputs.group label="Email address" for="email"> <x-inputs.text for="email" type="email"/> </x-inputs.group> <x-inputs.group label="Street address" for="street_address"> <x-inputs.text for="street_address"/> </x-inputs.group> <x-inputs.group label="City" for="city"> <x-inputs.text for="city"/> </x-inputs.group> <x-inputs.group label="State" for="state"> <x-inputs.text for="state"/> </x-inputs.group> <x-inputs.group label="Zip" for="zip"> <x-inputs.text for="zip"/> </x-inputs.group> </div> </div> </div> <div class="mt-8 border-t border-gray-200 pt-5"> <div class="flex justify-end"> <span class="ml-3 inline-flex rounded-md shadow-sm"> <button type="submit" class="inline-flex justify-center py-2 px-4 border border-transparent text-sm leading-5 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition duration-150 ease-in-out"> Save </button> </span> </div> </div> </form> @endsection
div
surrounding all of the components I changed the class ‘mt-6- to ‘space-y-6’ which evenly spaces out our elements vertically.web.php
and change your function that renders the view to the following. This will simulate retrieving some of the data from your database and passing it to the view:Route::get('profile', function(){ $data = [ 'first_name' => 'Shane', 'last_name' => 'Rosenthal', 'email' => '[email protected]', ]; return view('profile', compact('data')); });
text.blade.php
:@props([ 'for', 'type' => 'text', 'value' => '' ]) <div class="rounded-md shadow-sm"> <input type="{{ $type }}" value="{{ $value }}" {{ $attributes->merge(['class' => 'bg-white border border-gray-200 py-2 px-3 rounded-lg w-full block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5']) }} id="{{ $for }}" /> </div>
<x-inputs.text for="first_name" value="$data['first_name']"/>
<x-inputs.text for="first_name" :value="$data['first_name']"/>