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 Blog Template Laravel Package

tightenco/jigsaw-blog-template

A ready-to-use blog starter for Jigsaw, Tighten's Laravel-based static site generator. It includes Markdown-powered posts, Blade layouts, pagination, tags, and a clean structure for building fast, easily deployable static blogs with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Understand what you're getting first: this is a fork, not a dependency. There is no composer require tightenco/jigsaw-blog-template. The template's files become your codebase the moment you clone it, and there is no upgrade path — only diff-and-port against upstream. Internalize that before you type anything.

Prerequisites. PHP 8.2+, Composer, Node 20+ (Vite and Tailwind 4 both require it — v1.4.1 is the Vite/Tailwind 4 generation; older forks were Mix + Tailwind 3).

Bootstrap it:

git clone [email protected]:tighten/jigsaw-blog-template.git my-blog
cd my-blog
rm -rf .git            # you are starting your own repo, not tracking upstream
git init
composer install
npm install

Run it in two terminals (Jigsaw and Vite are separate processes):

# terminal 1
npm run dev            # Vite dev server / asset build

# terminal 2
./vendor/bin/jigsaw serve   # http://localhost:8000

Where to look first, in this order:

  1. config.php — site metadata, baseUrl, the posts collection definition, environment merging. This is the single file that explains the most about how the site is wired.
  2. package.json + vite.config.js — the asset entry points. Whatever paths appear as Vite inputs are the exact paths you pass to the template's asset helper in Blade.
  3. source/_layouts/ — master/post layouts. The Vite helper lives here; grep for vite in your layouts to discover the exact helper name, because it is template-owned code, not something Jigsaw ships.
  4. source/_posts/*.md — the content contract. Note the filename convention and the front matter keys.
  5. source/index.blade.php — the listing page; this is where you learn how the posts collection is exposed to Blade.

Your first end-to-end use case: ship one post. Create source/_posts/2025-08-01-hello-world.md:

---
extends: _layouts.post
section: content
title: Hello, world
date: 2025-08-01
author: Your Name
meta_description: A first post to prove the pipeline.
---

Body copy in Markdown.

Confirm it renders via jigsaw serve, appears on the index listing, then prove the production path works before you write a second post:

npm run build
./vendor/bin/jigsaw build production
# inspect build_production/ — open it with a static server, not the Jigsaw dev server

That last step is the one people skip, and it is the one that catches the failure modes below.

Implementation Patterns

Front matter is your data model. Every post is a Page object at render time; every front matter key becomes a property. Keep the contract small and consistent (title, date, author, meta_description, excerpt) and document it in the repo — a README table is enough. When you need a new field (canonical URL, og_image, draft), add it in the front matter of one post and read it in the layout with a null-safe fallback:

{{ $page->og_image ?? $page->baseUrl . '/assets/images/default-og.jpg' }}

Compose with layouts and partials, not copy-paste. source/_layouts holds page shells, source/_partials holds fragments (post card, header, footer). A listing page usually ends up as:

@foreach ($posts->take(5) as $post)
    @include('_partials.post-card', ['post' => $post])
@endforeach

and inside the partial, link with {{ $post->getPath() }} — never hand-build the URL from the filename. That keeps permalink changes contained to one place.

Add a second content type as a collection, not a hack. Changelogs, docs, and guides are just another collection in config.php with their own layout and their own index page. That's the cheap, idiomatic expansion:

'collections' => [
    'posts'     => ['path' => 'posts', 'sort' => '-date'],
    'changelog' => ['path' => 'changelog', 'sort' => '-date'],
],

Then source/changelog.blade.php loops $changelog. Do this before you're tempted to stuff docs into subdirectories of _posts.

Centralize site-wide values in config.php. Site title, author, social handles, and baseUrl belong there and get read from Blade ($page->site->…) rather than hardcoded in layouts. $page->baseUrl is the one you'll use constantly — prefix every absolute asset path with it so a move to a subdirectory deploy doesn't break every image.

Always resolve assets through the template's Vite helper. Never hardcode /assets/build/.... The helper reads the Vite manifest and returns the hashed filename, which differs between dev and production. If you add a new entry point, register it in vite.config.js and update the layout — the two must stay in sync or you'll get a missing-asset failure.

Use deploy previews as your editorial workflow. Because publishing is Git-based, the highest-leverage integration is per-PR preview deploys (Netlify, Cloudflare Pages, Vercel). Reviewers see the rendered post before merge, which is the closest thing this stack has to a CMS preview pane. Wire CI to run:

composer install --no-dev --no-interaction
npm ci
npm run build
./vendor/bin/jigsaw build production
# deploy build_production/

Extension points: bootstrap.php and the tasks/ directory. Jigsaw emits lifecycle events you can hook for post-build work — hashing assets, emitting an extra feed, generating redirect stubs. If you find yourself writing a shell script around the build, check whether it belongs in bootstrap.php first.

For anything request-time, decide now and externalize it. Search (Pagefind for static-only, Algolia if you need more), comments (giscus/Utterances), and forms (Formspree or a serverless function) are all "before launch or never" decisions. Bolting them on after the fact is where static-site projects go sideways.

Gotchas and Tips

The version number lies. v1.4.1 reads as a patch, but the payload is a toolchain replacement: Laravel Mix → Vite, Tailwind 3 → 4. There's no semver contract for downstream forks. If you're retrofitting an existing v1.4.0 fork, resource it as a breaking migration with a time-boxed spike and a discardable branch — and accept "stay on v1.4.0" as a legitimate outcome, because nothing here is a security fix.

Vite manifest breakage fails silently. A wrong manifest path produces HTTP 200 pages rendered completely unstyled. No exception, no log line, nothing in a unit test. Three defenses:

# does the manifest actually exist in the build output?
ls build_production/assets/build/.vite/manifest.json

plus a post-deploy smoke assertion that a known hashed CSS asset resolves, and visual regression on a representative page set. Also: you must run npm run build before jigsaw build production — running the Jigsaw build alone against a stale or absent manifest is the #1 way to ship an unstyled site.

Tailwind 4 is CSS-first. tailwind.config.js is largely gone; theme tokens live in @theme blocks in your CSS entry. If you carry custom theme extensions, plugins, safelists, or a design system pinned to Tailwind 3, that content does not port by copy-paste — hand-migrate it. Renamed utilities (shadow-sm → shadow-xs, etc.) produce missing styles, not errors.

If classes vanish in production, add an explicit source. Tailwind 4's automatic content detection respects .gitignore and starts from the CSS file's location. Most Jigsaw setups work out of the box, but when a utility used only in a Blade partial goes missing, an explicit @source "../../source"; in your CSS entry is the fix. Related: don't let the detector scan build_production/ — it'll balloon your CSS.

Pin Node in two places at once. .nvmrc and your CI/deploy image. Drift between a dev machine on Node 22 and a build image on Node 18 is a build failure that only appears in CI, usually at the worst moment.

Scheduled publishing and drafts don't exist. It's a static generator; "scheduled" means a CI cron that rebuilds. Drafts are a front-matter flag plus a build-time filter, or just a branch. Decide which and document it, or someone will publish a draft by merging a PR.

Date handling is a common papercut. Filenames conventionally encode YYYY-MM-DD-slug.md and front matter may carry a date too. Pick one as authoritative, keep them consistent, and parse defensively when formatting (Carbon::parse($post->date)->format('F j, Y')) so a future date format change doesn't throw in a Blade loop mid-build.

jigsaw serve doesn't watch everything. Changes to config.php and bootstrap.php typically need a restart, not just a refresh. If a config change "isn't taking," restart the server before you start debugging Blade.

Verify Markdown/Blade interop before relying on it. If you want to drop a Blade component or directive inside a post body, test it on one post and inspect the output. Don't build a content strategy that assumes it works.

Never commit build_* directories, and make sure composer install --no-dev in CI — the Jigsaw toolchain is a dev dependency and shouldn't be part of any deployed artifact anyway (the artifact is just static files).

Own the Vite helper explicitly. Jigsaw historically shipped a mix() helper; a Vite-based template has to supply its own Blade directive to bridge .vite/manifest.json. That helper is now your code. Assign it an owner, and re-validate it whenever you touch a layout, add a directive, or change deploy targets — it's the likeliest place for a silent break in the entire stack.

Review upstream quarterly, expect to skip. Treat tightenco/jigsaw-blog-template as reference documentation rather than a dependency with an SLA. Budget roughly a day per meaningful upstream change and default to "no" unless the change addresses something you actually hit.

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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings