PHPackages                             ojessecruz/simple-blog - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Parsing &amp; Serialization](/categories/parsing)
4. /
5. ojessecruz/simple-blog

ActiveLibrary[Parsing &amp; Serialization](/categories/parsing)

ojessecruz/simple-blog
======================

A Laravel lightweight blog to quickly publish and share ideas.

v0.4.3(1mo ago)011↓100%[2 PRs](https://github.com/ojessecruz/simple-blog/pulls)MITPHPPHP ^8.3CI passing

Since May 2Pushed 1mo agoCompare

[ Source](https://github.com/ojessecruz/simple-blog)[ Packagist](https://packagist.org/packages/ojessecruz/simple-blog)[ Docs](https://github.com/ojessecruz/simple-blog)[ GitHub Sponsors](https://github.com/ojessecruz)[ RSS](/packages/ojessecruz-simple-blog/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (14)Versions (12)Used By (0)

Simple Blog
===========

[](#simple-blog)

A ready-to-use Laravel blog — minimal reading-focused public listing, admin CRUD with Livewire, Markdown with HTML escaping, and draft preview in a new tab. Zero opinions on authentication: you plug it in via Laravel middleware (Gate, guard, or any combo).

Requirements
------------

[](#requirements)

- PHP 8.3+
- Laravel 11 / 12 / 13
- Livewire 3.5+
- Tailwind CSS in the consuming app (the package ships Tailwind classes, it does not compile its own CSS)

Installation
------------

[](#installation)

```
composer require ojessecruz/simple-blog
```

Publish and run the migrations:

```
php artisan vendor:publish --tag="simple-blog-migrations"
php artisan migrate
```

Publish the config (optional, but recommended):

```
php artisan vendor:publish --tag="simple-blog-config"
```

Publish the views (optional, only if you want to customize):

```
php artisan vendor:publish --tag="simple-blog-views"
```

Quickstart
----------

[](#quickstart)

Three steps and your blog is up.

### 1. Protect the admin routes

[](#1-protect-the-admin-routes)

The package does **not** embed any authorization logic. You plug it in via middleware in `config/blog.php`. Examples:

```
// Via a specific Gate
'admin_middleware' => ['web', 'auth', 'can:manage-blog'],

// Via a separate guard
'admin_middleware' => ['web', 'auth:admin'],

// Combo of your app's own middleware
'admin_middleware' => ['web', 'auth', 'verified', 'super.admin'],
```

If you go with a Gate, define it as usual in `AuthServiceProvider`:

```
Gate::define('manage-blog', fn ($user) => $user->is_admin === true);
```

### 2. Configure the author model

[](#2-configure-the-author-model)

Point it to your app's User:

```
// config/blog.php
'author_model' => App\Models\User::class,
```

And make User implement the `Author` contract:

```
use Jessecruz\SimpleBlog\Contracts\Author;

class User extends Authenticatable implements Author
{
    public function getBlogAuthorName(): string
    {
        return $this->name;
    }

    public function getBlogAuthorInitials(): string
    {
        $words = preg_split('/\s+/', trim($this->name)) ?: [];
        $initials = array_map(
            fn (string $w) => mb_strtoupper(mb_substr($w, 0, 1)),
            array_slice($words, 0, 2),
        );

        return implode('', $initials);
    }
}
```

### 3. Access it

[](#3-access-it)

- Public: `https://yourapp.com/blog`
- Admin: `https://yourapp.com/admin/blog`

Done.

Routes
------

[](#routes)

The package registers:

MethodURLNameDescriptionGET`/blog``blog.index`Public listingGET`/blog/category/{slug}``blog.category`Posts in a categoryGET`/blog/{slug}``blog.show`Individual postGET`/admin/blog``blog.admin.index`Admin list (filter/search)GET`/admin/blog/create``blog.admin.create`Create formGET`/admin/blog/{slug}/edit``blog.admin.edit`Edit formGET`/admin/blog/{slug}/preview``blog.admin.preview`Preview a draft/scheduled postGET`/admin/blog/categories``blog.admin.categories`Categories CRUDThe `/blog` and `/admin/blog` prefixes are configurable (`route_prefix`, `admin_route_prefix`).

Configuration
-------------

[](#configuration)

See `config/blog.php` (published) — every key has comments explaining what it does, with examples. Summary:

- **`route_prefix`** / **`admin_route_prefix`** — where to mount the routes
- **`public_middleware`** / **`admin_middleware`** — middleware stacks
- **`author_model`** — User model
- **`layouts.public`** / **`layouts.admin`** — Blade layouts wrapping the content
- **`cta_view`** — optional view rendered at the end of each post (e.g. pricing, newsletter)
- **`markdown`** — options passed to `Str::markdown()`

Customizing the layout
----------------------

[](#customizing-the-layout)

The package ships two neutral default layouts, both pulled from `config/blog.php`:

- **public** (`blog::layouts.public`) — used for `/blog` and `/blog/{slug}`. Plain HTML, no nav, content rendered through `@yield('content')`.
- **admin** (`blog::layouts.admin`) — used for `/admin/blog`. Slot-based, content rendered through `{{ $slot }}` so it can be pointed straight at modern Laravel layouts.

Most apps want the public side wrapped in their site chrome (header, nav, footer, SEO meta) and the admin inside their auth shell.

### Public layout: build it from scratch

[](#public-layout-build-it-from-scratch)

The simplest path is creating a dedicated layout for the blog with `@yield('content')`:

```
{{-- resources/views/layouts/blog-public.blade.php --}}

    @yield('title', config('app.name'))

    @stack('head') {{-- Required: lets the package emit SEO meta and JSON-LD --}}

    @vite('resources/css/app.css')

            {{ config('app.name') }}

        @yield('content')

        © {{ date('Y') }} {{ config('app.name') }}

```

Then point the config at it:

```
'layouts' => [
    'public' => 'layouts.blog-public',
    'admin' => 'blog::layouts.admin',
],
```

If your layout already loads its own CSS via `@vite`, set `'assets' => []` in `config/blog.php` to avoid loading it twice.

### Public layout: reuse a Blade component (slot-based)

[](#public-layout-reuse-a-blade-component-slot-based)

If your app already has a slot-based layout (Breeze, Jetstream, Folio, or a custom ``), create a small wrapper that bridges `@yield('content')` and `{{ $slot }}`:

```
{{-- resources/views/layouts/blog-public.blade.php --}}

    @yield('content')

```

```
'public' => 'layouts.blog-public',
```

Three lines of plumbing, your app's chrome wrapping the blog.

### Admin layout

[](#admin-layout)

The admin Livewire components use `->layout()` (slot-based), so the admin layout key can point at any slot-based view directly:

```
'admin' => 'layouts.app',  // your  view, no wrapper needed
```

If your admin layout is `@yield`-based instead, build a small slot bridge:

```
{{-- resources/views/layouts/blog-admin.blade.php --}}

    @vite('resources/css/app.css')
    @livewireStyles

    {{ $slot }}
    @livewireScripts

```

### Make Tailwind aware of the package views

[](#make-tailwind-aware-of-the-package-views)

If you use Tailwind CSS, add the package views to your `tailwind.config.js` `content` array so JIT compiles classes used in the package:

```
content: [
    // ... your existing paths ...
    './vendor/ojessecruz/simple-blog/resources/views/**/*.blade.php',
],
```

### Required directives in your layout

[](#required-directives-in-your-layout)

The package emits SEO meta tags, OG tags and JSON-LD via `@push('head')`. For these to render, your layout's `` must contain:

```
@stack('head')
```

Without it, `` and meta tags from posts won't appear in the head.

Customizing the visual style
----------------------------

[](#customizing-the-visual-style)

The package ships with `emerald` as the accent colour and `zinc` as the neutral. To brand it differently, publish the views and edit them directly. Three granularities are available:

```
# Everything (public + admin + layouts + icons)
php artisan vendor:publish --tag="simple-blog-views"

# Just the public side (index, show, public layout)
php artisan vendor:publish --tag="simple-blog-views-public"

# Just the admin side (Livewire admin views + admin layout)
php artisan vendor:publish --tag="simple-blog-views-admin"

# Just the layouts (public and admin shells, no inner content)
php artisan vendor:publish --tag="simple-blog-views-layouts"
```

The Blade files land in `resources/views/vendor/blog/`. From that point Laravel uses your published copies, so a global find-and-replace (`emerald-` → `blue-`, `emerald-` → `rose-`, etc.) is enough to retheme that slice.

Trade-off: published views are frozen at the version you published — bug fixes and new features that ship in later releases of the package won't reach them automatically. Publishing only the slice you actually want to customize (admin or public) keeps the rest tracking upstream.

Injecting a CTA into posts
--------------------------

[](#injecting-a-cta-into-posts)

Create a view (e.g. `resources/views/components/blog-cta.blade.php`) and point to it:

```
'cta_view' => 'components.blog-cta',
```

The view receives the `$post` variable and is rendered after the post content (on show) and below the feed (on index).

Models
------

[](#models)

The package exposes:

- `Jessecruz\SimpleBlog\Models\Post`
- `Jessecruz\SimpleBlog\Models\PostCategory`

Use them directly when needed (e.g. to generate a sitemap, export content):

```
use Jessecruz\SimpleBlog\Models\Post;

$posts = Post::published()->with('category')->latest('published_at')->get();
```

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

See [CHANGELOG](CHANGELOG.md).

License
-------

[](#license)

MIT — see [License File](LICENSE.md).

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance93

Actively maintained with recent releases

Popularity7

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

9

Last Release

36d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/e1de650c49d77234ee1d66c8c742a149ec30e99dbbe230035ae89cded326663b?d=identicon)[ojessecruz](/maintainers/ojessecruz)

---

Top Contributors

[![ojessecruz](https://avatars.githubusercontent.com/u/20089597?v=4)](https://github.com/ojessecruz "ojessecruz (27 commits)")

---

Tags

laravelmarkdownlivewireblogsimple-blogjessecruz

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/ojessecruz-simple-blog/health.svg)

```
[![Health](https://phpackages.com/badges/ojessecruz-simple-blog/health.svg)](https://phpackages.com/packages/ojessecruz-simple-blog)
```

###  Alternatives

[spatie/laravel-pdf

Create PDFs in Laravel apps

1.0k4.3M41](/packages/spatie-laravel-pdf)[filament/support

Core helper methods and foundation code for all Filament packages.

2328.3M213](/packages/filament-support)[spatie/laravel-markdown-response

Serve markdown versions of your HTML pages to AI agents and bots

7339.6k4](/packages/spatie-laravel-markdown-response)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3913.7k](/packages/rawilk-profile-filament-plugin)[tomshaw/electricgrid

A feature-rich Livewire package designed for projects that require dynamic, interactive data tables.

119.2k](/packages/tomshaw-electricgrid)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
