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

Sage Laravel Package

roots/sage

Sage is a modern WordPress starter theme from Roots, bringing Laravel-style Blade templates, Acorn, Tailwind CSS, and Vite for a fast, elegant development workflow and cleaner, more maintainable theme code.

View on GitHub
Deep Wiki
Context7

Getting Started

Sage is a WordPress starter theme that borrows Laravel idioms — Blade, Composer autoloading, Acorn service providers, Vite, and Tailwind — rather than a package you install into a Laravel app. Treat it as a theme scaffold you fork and own.

Minimal setup

composer create-project roots/sage my-theme
cd my-theme
npm install
npm run dev

Then place or symlink the theme into wp-content/themes/, activate it in WordPress, and open the site. npm run dev starts Vite with HMR; Blade/PHP changes trigger a full reload if refresh: true is enabled in vite.config.js.

Where to look first

  • resources/views/layouts/app.blade.php — the document shell, wp_head(), wp_footer(), and the @vite(...) entry points.
  • resources/views/ — WordPress template hierarchy as Blade files: index.blade.php, single.blade.php, page.blade.php, archive.blade.php, search.blade.php, 404.blade.php.
  • resources/views/components/ and resources/views/partials/ — reusable Blade components and partials.
  • app/setup.php and app/filters.php — theme supports, menus, and WordPress hooks.
  • app/Providers/ThemeServiceProvider.php — Acorn bootstrapping and service provider registration.
  • vite.config.js, package.json, tailwind.config.js, composer.json — build and dependency surface.

First use case

Convert a page to Blade and style it with Tailwind:

  1. Create resources/views/page.blade.php.
  2. Extend layouts.app, define a content section, and use Tailwind classes.
  3. Visit the page with npm run dev running.
  4. Build production assets with npm run build before deploying.
{{-- resources/views/page.blade.php --}}
@extends('layouts.app')

@section('content')
  <main class="mx-auto max-w-3xl px-4 py-12">
    <h1 class="text-3xl font-bold">{{ get_the_title() }}</h1>
    <div class="prose mt-6">{!! get_the_content() !!}</div>
  </main>
@endsection

Implementation Patterns

Blade layouts and template hierarchy

Sage keeps WordPress template naming, but the files are Blade. A single-{post_type}.blade.php or page-{slug}.blade.php is picked up by the normal WordPress hierarchy. Keep wp_head() and wp_footer() in the layout, and use @yield/@section for page content.

{{-- resources/views/layouts/app.blade.php --}}
<!doctype html>
<html @php(language_attributes())>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    @php(wp_head())
    @vite(['resources/css/app.css', 'resources/js/app.js'])
  </head>
  <body @php(body_class())>
    @yield('content')
    @php(wp_footer())
  </body>
</html>

Components for repeated UI

Use anonymous components for presentational pieces and class components when you need logic. This is the closest day-to-day equivalent to Laravel Blade components.

{{-- resources/views/components/card.blade.php --}}
@props(['title', 'href' => null])

<div {{ $attributes->merge(['class' => 'rounded-lg border p-6']) }}>
  <h2 class="text-lg font-semibold">{{ $title }}</h2>
  <div class="mt-2 text-gray-600">{{ $slot }}</div>
  @if ($href)
    <a class="mt-4 inline-block text-blue-600" href="{{ $href }}">Read more</a>
  @endif
</div>
<x-card title="Hello" href="{{ get_permalink() }}">
  {!! get_the_excerpt() !!}
</x-card>

View composers for data

Rather than scattering get_field() and WP_Query calls across templates, put data preparation in app/View/Composers. A composer declares which views it targets and returns data from with().

namespace App\View\Composers;

use Roots\Acorn\View\Composer;

class PageComposer extends Composer
{
    protected static $views = ['page', 'page-*'];

    public function with(): array
    {
        return [
            'subtitle' => get_field('subtitle') ?: '',
            'related' => get_posts([
                'post_type' => 'post',
                'posts_per_page' => 3,
            ]),
        ];
    }
}

If a composer does not appear to run, check how composers are registered in your Acorn configuration or ThemeServiceProvider.

WordPress loops in Blade

For simple loops, use the Acorn Blade directives where available (@posts, @endposts, @query) or set up a WP_Query in a composer and iterate it in Blade. Keep the loop markup in partials so archive, search, and index templates can share it.

@query(['post_type' => 'post', 'posts_per_page' => 5])
  @posts
    @include('partials.card', ['post' => $post])
  @endposts
@endquery

Vite entry points

Add new CSS/JS entry points in vite.config.js, then reference them with @vite([...]) in the layout or a specific template. Do not hard-code /dist or /build URLs.

// vite.config.js
import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin'

export default defineConfig({
  plugins: [
    laravel({
      input: ['resources/css/app.css', 'resources/js/app.js'],
      refresh: true,
    }),
  ],
})

Run npm run dev during development and npm run build for production. HMR handles CSS/JS; Blade changes reload the page when refresh: true is set.

Tailwind configuration

Point Tailwind’s content globs at every place class names can appear: Blade views, PHP under app/, and JS. If you construct class names dynamically, add them to safelist or use complete class strings in the source.

// tailwind.config.js
export default {
  content: [
    './resources/**/*.blade.php',
    './resources/**/*.js',
    './app/**/*.php',
  ],
  theme: { extend: {} },
  plugins: [],
}

WordPress hooks and Acorn

Put add_action/add_filter calls in app/setup.php or app/filters.php, not in Blade templates. Use Acorn service providers for larger integrations, custom Blade directives, or bindings. For a Laravel developer this is the familiar service-provider pattern applied to WordPress hooks.

// app/setup.php
add_action('after_setup_theme', function () {
    add_theme_support('title-tag');
    add_theme_support('post-thumbnails');
    register_nav_menus([
        'primary' => __('Primary Navigation', 'sage'),
    ]);
});

Menus, search, and forms

WordPress functions that echo need to be captured or invoked directly in Blade. For menus:

@php
  wp_nav_menu([
    'theme_location' => 'primary',
    'container' => false,
    'menu_class' => 'flex gap-4',
  ]);
@endphp

For search, either call get_search_form() or maintain a Blade search form. If you override the search form or search results template, remember that upstream fixes — such as the v11.2.1 quotation-mark handling — will not apply automatically.

Typical workflow

  1. Add or edit a Blade template in resources/views/.
  2. Move data retrieval into a composer or component class if the template starts accumulating PHP.
  3. Add Tailwind classes, run npm run dev, verify HMR.
  4. Run npm run build and commit the built assets if your deployment requires them.
  5. Clear caches with wp acorn optimize:clear when views or config changes are not reflected.
  6. Deploy the theme directory as a normal WordPress theme.

Gotchas and Tips

Sage is a fork, not a dependency

Upstream releases are merges or cherry-picks into your theme, not composer update events. Assign an owner and review upstream monthly. Major versions have historically changed build tooling and Acorn coupling, so budget for periodic rebuild work.

Vite manifest errors

In production, @vite(...) reads the manifest generated by npm run build. If the build step is skipped or stale, the site fails at runtime. Clear public/build and rebuild; ensure CI runs npm run build before deployment.

HMR and local URLs

If HMR connects but assets fail, check that the Vite dev server host/port matches how you access WordPress. Misconfigured server.host or hmr.host causes mixed-content or connection errors. Also confirm WP_HOME/WP_SITEURL are consistent with the URL in the browser.

Blade view cache

Compiled Blade views can go stale after deploys or provider changes. Use wp acorn optimize:clear (with WP-CLI and Acorn available) or clear the relevant storage/framework/views directory. If wp acorn is unavailable, check that WP-CLI is installed and Acorn is bootstrapped.

Template hierarchy surprises

A Blade template is only used if its filename matches the WordPress hierarchy. If page-about.blade.php is ignored, verify the page slug, whether a more specific template exists, and whether a filter in app/filters.php overrides template selection. Remember that index.blade.php is the fallback.

Tailwind purge misses dynamic classes

Classes built by string concatenation — "text-{{ $color }}-500" — will not be detected. Use full class names in source, a mapping array, or safelist. This is the most common “why is my style missing in production but present in dev” issue.

Escaping and echoing WordPress functions

Use {{ }} for escaped output and {!! !!} only for trusted HTML. Many WordPress functions echo directly (the_title(), the_content(), wp_nav_menu()), so call them inside @php(...) or capture with output buffering. Avoid mixing raw PHP and Blade unnecessarily.

Search form overrides and the v11.2.1 patch

The v11.2.1 release is a narrow fix for quotation marks in the search query field. If your theme overrides searchform.blade.php, search.blade.php, or custom search result markup, you will not inherit it. Port the change manually and smoke test quoted phrases, apostrophes, and empty queries.

Composer autoload after new classes

After adding classes under app/, run composer dump-autoload if they are not being discovered. This is especially relevant for new composers, components, or service providers.

Do not edit built assets

Treat public/build as generated output. Make changes in resources/ and rebuild. Manually editing hashed assets creates drift and breaks on the next build.

Extension points

Useful places to extend without fighting the theme:

  • Custom Blade directives in a service provider via Blade::directive(...).
  • Custom Acorn service providers in app/Providers/, registered in your Acorn config.
  • Custom Vite plugins in vite.config.js for asset processing or aliases.
  • View composers for data shaping and WordPress query logic.
  • Blade components for reusable UI and form controls.
  • app/filters.php for WordPress filter customization.

Debugging checklist

  • Blank page after deploy: check Vite manifest, PHP version, and Acorn/PHP compatibility.
  • Styles missing only in production: inspect Tailwind content globs and run a production build locally.
  • Template not applying: confirm the WordPress hierarchy filename and inspect template-selection filters.
  • Hooks not firing: verify app/setup.php/app/filters.php are loaded and that the hook name and priority are correct.
  • Data missing in a view: confirm the composer targets the right view name and that with() returns the expected keys.
  • Cache confusion: clear Acorn/Blade caches and any WordPress object/page cache before deeper debugging.

Version compatibility

Sage 11 expects PHP 8.1+, a modern WordPress 6.x, Composer, Node LTS, and Acorn. Check these before merging upstream changes or upgrading the host. Node and PHP drift are common causes of build failures that look like theme bugs.

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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo