Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Jigsaw Laravel Package

tightenco/jigsaw

Jigsaw is a simple static site generator powered by Laravel's Blade templating engine. Build fast, framework-free websites using familiar Blade components, Markdown content, and collections, all without needing a database or server.

View on GitHub
Deep Wiki
Context7

Getting Started

Jigsaw is a static site generator that compiles Blade templates (plus Markdown) into plain HTML. If you know Blade, you already know 80% of Jigsaw — the mental model is "Laravel views, but the output is files on disk instead of a response."

Minimal setup:

composer require tightenco/jigsaw --dev
./vendor/bin/jigsaw init          # choose `starter`, `blog`, or `tailwindcss`
./vendor/bin/jigsaw serve         # http://localhost:8000, rebuilds on save

Build for deployment:

./vendor/bin/jigsaw build production   # -> build_production/
./vendor/bin/jigsaw build              # -> build_local/ (default, "local" env)

Most teams add Composer scripts so nobody memorizes vendor/bin paths:

"scripts": {
  "dev": "jigsaw serve",
  "build": "jigsaw build production"
}

Where to look first

  • source/ — your site. Everything here becomes a page unless the folder/file is prefixed with _.
  • source/_layouts/ — Blade layouts (master.blade.php, post.blade.php).
  • source/_partials/ — reusable includes.
  • config.php (project root) — baseUrl, collections, build behavior.
  • bootstrap.php — global hook point for custom Blade directives and build events.
  • webpack.mix.js / package.json — asset pipeline (Laravel Mix), if the starter uses it.

First use case: add source/about.blade.php with a front-matter block, extend the master layout, fill a section, run jigsaw serve, and confirm it appears at /about.html (or /about/ with --pretty). Then do the same page as about.md with extends: _layouts.master in YAML front matter — that's the whole Markdown workflow.

Implementation Patterns

Layout + section inheritance. Standard Blade, exactly as in Laravel:

{{-- source/_layouts/master.blade.php --}}
<html>
  <head><title>@yield('title', $page->title)</title></head>
  <body>@yield('content')</body>
</html>
{{-- source/about.blade.php --}}
---
title: About
---
@extends('_layouts.master')

@section('content')
  <h1>{{ $page->title }}</h1>
@endsection

Markdown pages. Front matter is YAML delimited by --- at the very top of the file. extends names the layout and section names the yield it fills:

---
title: Hello world
date: 2026-05-01
extends: _layouts.post
section: content
---
Body text here, with **Markdown** and Blade: {{ $page->title }}.

Collections are the workhorse for blogs, docs, changelogs, and any repeated content type. Declare them in config.php, then they're available as a variable in every view:

// config.php
return [
    'baseUrl' => getenv('APP_URL') ?: 'http://localhost:8000',
    'collections' => [
        'posts' => [
            'extends' => '_layouts.post',
            'items' => ['posts/*.md'],
            'sort' => 'date',
        ],
    ],
];
@foreach ($posts->sortByDesc('date') as $post)
  <a href="{{ $post->getPath() }}">{{ $post->title }}</a>
@endforeach

Collections are collections in the Laravel sense — filter, where, sortBy, first, take all work. Filtering by front-matter fields ($posts->where('featured', true)) is the standard way to build "featured posts" or tag pages without plugins.

Pagination. Call paginate() on the collection and render links(); override the markup by dropping a paginator view into source/_paginators/.

@foreach ($posts->paginate(5) as $post)
  ...
@endforeach
{{ $posts->links() }}

Partials as components. @include('_partials.card', ['post' => $post]) is the idiomatic way to pass data — there are no props/slots beyond what Blade gives you, so keep partials dumb and pass everything explicitly.

Custom Blade directives & build hooks. bootstrap.php is loaded before the build and receives $bladeCompiler and $events:

// bootstrap.php
$bladeCompiler->directive('datetime', function ($expression) {
    return "<?php echo ($expression)->format('M j, Y'); ?>";
});

$events->listen(\TightenCo\Jigsaw\Events\BuildStarted::class, function ($event) {
    // e.g. regenerate a sitemap, copy files, purge a CI cache
});

This is the sanctioned extension point — reach for it before forking a starter.

Assets. Keep source assets in source/_assets/ (underscore = not published), build them with Mix, and reference the compiled output. Jigsaw's Tailwind starter wires this up; follow its structure rather than inventing one.

Environments and deployment. build_local for jigsaw serve, build_production for CI. Set baseUrl from an env var in config.php so canonical URLs, feeds, and pagination links are correct on the deployed host. A typical pipeline is: composer install --no-dev && npm ci && npm run prod && jigsaw build production, then publish build_production/ to Netlify, S3+CloudFront, GitHub Pages, or any static host.

Gotchas and Tips

  • Underscore prefix means "not published." _layouts, _partials, _assets are skipped during the build. Rename a folder and you'll suddenly publish it — this is the most common accidental leak.
  • Front matter must be the first bytes of the file. A leading blank line or BOM silently disables parsing, and you get a page with no title and no layout.
  • Blade runs over Markdown. Writing {{ something }} inside a code sample will be evaluated. Escape it as @{{ something }} (or wrap in @verbatim) — this bites everyone writing docs about Blade.
  • Pretty URLs are a site-wide decision. --pretty changes output from about.html to about/index.html, and every internal link has to match. Use $page->getPath() / collection item URLs rather than hand-written hrefs, and apply the flag consistently between serve and the production build. Pagination links in particular rely on this.
  • baseUrl is not cosmetic. Feeds, canonical tags, og: tags, and paginator links all build from it. Hardcoding localhost in config.php and shipping it is a classic post-deploy bug.
  • Config and bootstrap.php changes need a restart of jigsaw serve. The watcher picks up source/ edits, not changes to the PHP bootstrap/config layer.
  • Debugging build-time state: there's no browser devtools in the build, so temporarily drop @php dump($posts->pluck('title')); @endphp (or dd()) into a template and read the CLI output. It renders into the artifact, so remove it before committing.
  • Sort dates as strings. sortByDesc('date') is lexicographic unless you normalize front matter to YYYY-MM-DD. Mixed formats (May 1, 2026 vs 2026-05-01) produce scrambled ordering that looks like random behavior.
  • $page is the current page; collection items are their own objects. $page->url is the absolute-ish URL, $page->getPath() the path without extension — pick the right one when building nav and active-link states.
  • Give yourself a 404. source/404.blade.php compiles to 404.html; most static hosts pick it up automatically.
  • Gitignore build_local/ and build_production/. They're derived artifacts, and committing them turns every deploy into a merge-conflict generator.
  • Starter code is the real documentation. jigsaw init blog produces a working reference implementation (collections, pagination, feeds, Mix) that's more current than any tutorial — read it before building your own structure from scratch.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console