Creating Your First Blog With TALL - Part Five
Tags: tailwindcss, alpinejs, laravel, livewire
Published: 2021-03-29 22:43:00

Hello and welcome to this episode of the tutorial series. In the previous episode you learnt how to create factories and seed our database with sample data. This episode will begin with building the UI part of our blog using Laravel Livewire components.

At the end of the tutorial, you should be able to:

-   Describe what a Livewire component is.
-   Create and modify Livewire components to suit your needs.
-   Use Tailwind CSS utility classes to practically layout elements in your Blade view files.

As usual, fasten up your seat belt and let's dive in. :)

**Note**: There is a starter project up to this episode which can be accessed on Github [here](https://github.com/nayi10/tall-blog)

## Introduction
### Part Two Recap

Remember in part two of this tutorial series we configured the public facing routes to return the default Laravel Jetstream **welcome** page. If you also remember, there were exactly two routes for the public facing pages of our blog - the home and the category pages. The homepage will be a listing of our blog posts while the category page will list all blog posts of a particular category. You can always read part two of this tutorial series [here](https://blog.bandughana.com/posts/creating-your-first-blog-with-tall-part-two) if you missed that part.

You're going to work on those two pages and replace the **welcome** page with a more interesting listing of your blog posts for this episode, albeit with introduction to some Tailwind CSS utilities. OK. Read on.

### Introduction to Livewire Components

Livewire components are reusable pieces of code that you can define once and use in different parts of your application. They are just like Laravel components but comes with the power of both Laravel and Livewire. By default, a Livewire component is created with a corresponding view file. Livewire component classes are also placed in the **app/Http/Livewire** directory while their corresponding views are placed in the **resources/views/livewire** directory.

### Creating Livewire Components

Livewire components are created by running the `make:livewire` or `livewire:make` Artisan commands in your terminal:
```
php artisan make:livewire PostItem
// or ...
php artisan livewire:make PostItem
```
This creates the most basic Livewire component, which extends the base Livewire `Component` class and contains a `render` method - used for rendering views and inline text:
```
<?php // tall-blog/app/Http/Livewire/PostItem.php 
namespace App\\Http\\Livewire;

use Livewire\\Component;

class PostItem extends Component {
    public function render() {
        return view('livewire.post-item');
    }
}
```
and the corresponding view:
```
{{\-- tall-blog/resources/views/livewire/post-item.blade.php --}}
<div>
    {{\-- If you look to others for fulfillment, you will never truly be fulfilled. --}}
</div>
```
Adding the `--inline` option tells Livewire that your component is an *inline* component and won't be returning any view in its `render` method. So this:
```
php artisan make:livewire PostItem --inline
```
yields something like this:
```
...
public function render() {
    return <<<'blade'
            <div></div>
        blade;
}
...
```
It is important to understand that `public` properties in a Livewire component are readily available in the component's view file, so you don't need to pass it through the `view` method like you're used to do if you've worked with pure Laravel controllers:
```
// This:
class PostItem extends Component {
    public $post;﻿
    public function render() {
        return view('livewire.post-item');
    }﻿
}

// is the same as this:
﻿class PostItem extends Component {
    public $post;﻿
    public function render() {
        return view('livewire.post-item', ['post' => $post]);
    }﻿
}
```
### Rendering Livewire Components

Livewire components are meant to be reusable. As a result, you can use them anywhere you would a Laravel component. Rendering a component can be done by either using the `<livewire:component-name />` tag syntax or by using the `@livewire('component-name')` blade directive syntax:
```
<div>
    <livewire:post-item />
</div>
{{-- Or --}}
<div>
    @livewire('post-item')
</div>
```
### Passing Parameters to Components

You can pass parameters to a component by specifying those parameters like so:
```
<livewire:post-item :post="$post" />
{{-- or --}}
@livewire('post-item', ['post' => $post])
```
Now the `$post` variable will be available in the **PostItem** component and view.

### Accessing Route Parameters

In a situation whereby you need to access route parameters like you would in a traditional Laravel controller, Livewire allows you to do that in the `mount` method:
```
class MyComponent extends Component {
    public $userId;

    public function mount($userId) {
        $this->userId = $userId;
    }
﻿
    public function render() {
        return view('livewire.my-component');
    }
}
```
This feature makes it so powerful for Livewire components to mimic the behavior of Laravel controllers and also makes it easy to make full page components.

Creating Our Blog's Components
------------------------------

Now that you have a solid understanding of Livewire components, let's move on to work on our blog. If you haven't yet done so from above, create your first Livewire component for the project by running this command in your terminal:
```
php artisan make:livewire PostItem
```
This component represents one post item from a list in the home and category pages. Open the component class in **app/Http/Livewire/PostItem.php** and add a public `$post` property to the beginning. The component class should look similar to this:
```
<?php // tall-blog/app/Http/Livewire/PostItem.php
namespace App\Http\Livewire;

use Livewire\Component;

class PostItem extends Component {
    public $post;

    public function render() {
        return view('livewire.post-item');
    }
}
```
As pointed out above, the `$post` property will now be readily available in the view file for use since it is declared `public`. Open the corresponding blade view file in **resources/views/livewire/post-item.blade.php**. Make sure it contains the following:
```
{{-- tall-blog/resources/views/livewire/post-item.blade.php --}}
<article class="flex flex-col mb-2 rounded-md shadow-md md:mb-0">
    <a href="{{ route('post-detail', ['slug' => $post->slug]) }}">
        <img src='{{ asset("storage/posts/$post->featured_image") }}' 
            alt="{{ $post->title }}" class="w-full h-56 rounded-t-md">
    </a>
    <div class="p-3">
        <h3 class="text-lg font-semibold text-gray-900">
            <a href="{{ route('post-detail', ['slug' => $post->slug]) }}">
                {{ $post->title }}
            </a>
        </h3>
        <p class="text-gray-800">
            <a href="{{ route('post-detail', ['slug' => $post->slug]) }}">
                {{ $post->excerpt }}
            </a>
        </p>
        <div class="flex flex-row justify-between mt-2">
            <a href="{{ route('category', ['category' => $post->category]) }}" 
                 class="px-2 text-sm text-indigo-900 bg-indigo-100 rounded">
                {{ $post->category }}
            </a>
            <small>
                {{ $post->published_date }}
            </small>
        </div>
    </div>
</article>
```
First of all, you can see that we're making use of the `$post` variable in this file, which contains a blog post entry. We're utilizing Tailwind CSS class utilities to style the root `<article>` element. In this case, we have laid its contents in a [Flexbox](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox) with a `flex-direction` of `col` - meaning we want to lay the contents from the top to bottom. `mb-2` is a utility class for 8-pixels margin-bottom value with `rounded-md` and `shadow-md` representing medium rounded and medium shadowed shapes respectively.

Next is the anchor element (`a` tag). We're referencing a named Route to route to the post detail page(using the `route` helper function) which uses the post's slug to construct the link. Remember we didn't create the post detail route so you would need to add it just below the homepage route:
```
Route::get('/', function () {
     return view('welcome');
})->name('home');

// Add below route
Route::get('{slug}', function ($slug) {
    return view('welcome');
})->name('post-detail');
```
Next in the tree is the image, whose width we have made full (100%) with `w-full` and whose height we have decided will be `h-56` (equivalent to 224px) with its top rounded. The post image's `src` attribute points to an image in the **posts** folder which is in the `storage` symlink we created earlier. `asset` is also one of the helper functions that allows you to display assets(css, images, js) files in your blade views. The file itself is uploaded in the **posts** folder while its name is saved into the database.

The next block contains the title, excerpt, category and published date all wrapped in a nicely padded(with `p-3`) `div` element. Both the title and excerpt use a different shade of gray text color from Tailwind CSS's color utilities. The last thing worth noting is the fact that the category and published date of our post are also wrapped in a Flexbox `div` container which runs from left to right (`row`) with a `justify-between` utility which ensures that these two elements are placed at the extreme ends of both left and right of the containing `div` element.

- - -

The post item component is now done. Let's create another component that will list all the posts on the homepage. This component will fetch all the posts from the database and display each in the post item component we created above. Enter the command to create the **show-posts** component:
```
php artisan make:livewire ShowPosts
```
Open the created component at **app/Http/Livewire/ShowPosts.php** and make sure its contents is equal to this:
```
<?php // tall-blog/app/Http/Livewire/ShowPosts.php
namespace App\Http\Livewire;

use App\Models\Post;
use Livewire\Component;

class ShowPosts extends Component {
    public $posts;

    public function mount() {
        $this->posts = Post::where('is_published', true)->get();
    }
    public function render() {
        return view('livewire.show-posts')->layout("layouts/guest");
    }
}
```
The `$posts` property holds all our posts from the database, which we fetch when this component is mounted (this is why the query is put in the `mount` method). We're only interested in posts that are published. We have also specified the layout to use (i.e **guest** layout file). By default, Livewire uses the **resources/views/layouts/app.blade.php** layout file. However, because Jetstream adds authentication and other checks to that layout, using the app layout file without those checks in place will lead to errors, that's the reason we're using the **resources/views/layouts/guest.blade.php** layout file.

Now open the corresponding **resources/views/livewire/show-posts.bladed.php** blade view and enter the following in it:
```
<div class="gap-4 m-2 md:grid md:grid-cols-2 lg:grid-cols-4">
    @foreach ($posts as $post)
        @livewire('post-item', ['post' => $post], key($post->id))
    @endforeach
</div>
```
This one is simpler than you might have guessed. We're looping through the `$posts` collection and rendering each using the `PostItem` component we created earlier, passing the `$post` to its `post` parameter and giving its key the post id. The key is used to uniquely differentiate the current post item from the others and is very necessary when you, for example, want to toggle a post's visibility.

On the Tailwind part, we're displaying the posts in

1.  a block container on small screens,
2.  a grid container with two columns in medium screen devices and
3.  a grid container with four columns in large+ screen devices

All these with a 16-px gap between each item. Of course, and an 8-pixel margin.

Now change the homepage route to the following:
```
...
Route::get('/', App\Http\Livewire\ShowPosts::class)->name('home');
...
```
Hook up your terminal, run `php artisan serve` and open your browser to http://127.0.0.1:8000 and you should see posts in the homepage:

![Blog Homepage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lsk9h4bwzm7uzfoo6e16.png)

Now create the last Livewire component, which will display all posts from a particular category, by running this Artisan command:
```
php artisan make:livewire CategoryPosts --inline
```
We have made the `CategoryPosts` component inline because it is going to reuse the **show-posts** component view file. Since there is nothing different between the two except filtering from the database, it is simpler using this approach.

Modify the `CategoryPosts` component to match the following:
```
<?php 
// tall-blog/app/Http/Livewire/CategoryPosts.php

namespace App\Http\Livewire;

use App\Models\Post;
use Livewire\Component;

class CategoryPosts extends Component {
    public $posts;

    public function mount($category) {
        $this->posts = Post::where('category', $category)
             ->where('is_published', true)
             ->get();
    }
    public function render() {
        return view('livewire.show-posts')
                 ->layout("layouts/guest");
    }
}
```
This is almost the same as the `ShowPosts` component except that we've changed the query to fetch based on the given category query string passed to the `mount` method. Change the category route to the following:
```
...
Route::get('categories/{category}', CategoryPosts::class)->name('category');
...
```
Start the built-in PHP server if it's not running:
```
php artisan serve
```
Now go back to your homepage and click on any category. It should send you to the category page displaying posts in the same category:

![Category Page Showing the est Category](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zr7jubt2xtij5gkdws9r.png)

This brings us to the end of this episode. I hope you really enjoyed the tutorial and I'd love to see you reading the next episode. We post updates on both our [Twitter](https://twitter.com/bandughana/) and [Facebook](https://www.facebook.com/bandughana) pages. If you don't mind, you can follow any of our social media pages to get notified when a new post is published.

Twitter: [https://twitter.com/bandughana](https://twitter.com/bandughana/)

Facebook: [https://www.facebook.com/bandughana](https://www.facebook.com/bandughana)

Thank you and see you for the next post.