tzsmm/next-blade

Compile Next.js static HTML exports into Laravel Blade templates with dynamic variable injection, favicon replacement, and native view caching.

Maintainers

Package info

github.com/tzsmm/next-blade

pkg:composer/tzsmm/next-blade

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-08-21 09:33 UTC

This package is auto-updated.

Last update: 2026-08-21 09:38:02 UTC


README

Latest Version Total Downloads License PHP Version Laravel Version

NextBlade

Use Next.js as a frontend builder for Laravel Blade.

NextBlade bridges Next.js and Laravel by syncing statically exported HTML pages into Laravel's resources/views/ directory and registering them as Blade templates. You write Blade variables and directives directly in your React/TSX components — {{ $title }}, @if($user), @foreach($items as $item) — and Laravel's native Blade engine compiles everything at runtime.

No custom compiling. No SEO injector. No variable replacement layer. Just Blade.

How It Works

┌─────────────────┐     next build      ┌──────────────┐     next-blade:build     ┌─────────────────────┐
│   Next.js App   │  ───────────────►   │   out/       │  ──────────────────────►  │  Laravel Blade Views │
│   (React/TSX)   │   static export     │  (HTML/CSS)  │   prepare + sync         │  (resources/views/)  │
└─────────────────┘                     └──────────────┘                           └─────────────────────┘
                                                                                           │
                                                                                           ▼
                                                                                   ┌─────────────────────┐
                                                                                   │  Laravel serves HTML │
                                                                                   │  with Blade compiled │
                                                                                   │  (any variable, any  │
                                                                                   │   directive, full    │
                                                                                   │   Blade power)       │
                                                                                   └─────────────────────┘
  1. You build your frontend with Next.js using output: 'export' (static HTML export).
  2. You write any Blade syntax directly in your Next.js components — {{ $title }}, {!! $content !!}, @if, @foreach, @include, etc.
  3. NextBlade prepares the exported HTML for Blade (escapes JSON-LD @ symbols, decodes HTML entities) and syncs files to resources/views/frontend/.
  4. Laravel serves these views using its native Blade engine with full view caching and all standard Blade features.

Features

  • Full Blade support — use any Blade variable ({{ }}), raw expression ({!! !!}), or directive (@if, @foreach, @include, etc.) directly in your Next.js components
  • Safe @ symbol escaping — JSON-LD @context, @type, @id won't crash the Blade compiler (auto-escaped to @@)
  • HTML entity decoding — Next.js encodes quotes as ' / " which are automatically decoded inside Blade expressions
  • Laravel view caching — compiled templates are cached by Laravel's native engine, no custom caching needed
  • Artisan commandsnext-blade:build to sync, next-blade:init to scaffold a new frontend
  • Node.js script includednpm run build auto-syncs after Next.js builds (no artisan needed)
  • Configurable — every path and behavior can be customized via config/next-blade.php
  • Zero runtime dependencies — no Node.js server needed in production, pure PHP/Laravel

Requirements

Requirement Version
PHP >= 8.1
Laravel 10, 11, 12, or 13
Node.js >= 18 (for building the frontend)
Next.js >= 14 (with output: 'export')

Installation

1. Install via Composer

composer require tzsmm/next-blade

The service provider is auto-discovered. No manual registration needed.

2. Publish the config file

php artisan vendor:publish --tag=next-blade-config

This creates config/next-blade.php where you can customize all paths and behaviors.

3. Scaffold a new frontend (optional)

If you're starting fresh and don't have a frontend/ directory yet:

php artisan next-blade:init

This creates a pre-configured Next.js project at frontend/ with:

  • next.config.ts (static export enabled)
  • package.json (build + sync scripts)
  • scripts/build-to-laravel.js (Node.js sync companion)
  • Starter src/app/page.tsx and layout.tsx

Then install dependencies:

cd frontend
npm install

Usage

Writing Blade in Next.js

Write Blade syntax directly in your Next.js components. It will survive the static export and Laravel will compile it:

// src/app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <head>
        <title>{'{{ $title }}'}</title>
        <meta name="description" content="{{ $meta_description }}" />
        <meta property="og:title" content="{{ $og_title }}" />
        <meta property="og:image" content="{{ $og_image }}" />
        <link rel="canonical" href="{{ $canonical_url }}" />
        <link rel="icon" href="{{ $settings['favicon'] ?? '/favicon.ico' }}" />
      </head>
      <body>{children}</body>
    </html>
  );
}
// src/app/page.tsx
export default function Home() {
  return (
    <div>
      <h1>{'{{ $page_title }}'}</h1>

      {/* Use any Blade directive */}
      <div dangerouslySetInnerHTML={{ __html: `
        @if($show_banner)
          <div class="banner">{{ $banner_text }}</div>
        @endif

        @foreach($features as $feature)
          <div class="feature">
            <h3>{{ $feature['title'] }}</h3>
            <p>{{ $feature['description'] }}</p>
          </div>
        @endforeach
      `}} />
    </div>
  );
}

Build and sync

Option A — From the frontend directory:

cd frontend
npm run build

This runs next build followed by the sync script automatically.

Option B — From the Laravel root:

php artisan next-blade:build --npm

This runs the full pipeline (npm build + sync) in one command.

Option C — Sync only (skip npm build):

php artisan next-blade:build

Or from the frontend:

npm run sync

Serve with Laravel

In your routes/web.php, return the compiled views with any data you want:

Route::get('/', function () {
    return response(view('frontend.index', [
        // SEO
        'title'            => 'My App — Welcome',
        'meta_description' => 'A description for search engines.',
        'og_title'         => 'My App',
        'og_image'         => 'https://example.com/og-image.png',
        'canonical_url'    => 'https://example.com/',

        // Dynamic content
        'page_title'   => 'Welcome to My App',
        'show_banner'  => true,
        'banner_text'  => 'Limited time offer!',
        'features'     => [
            ['title' => 'Fast', 'description' => 'Built for speed.'],
            ['title' => 'Secure', 'description' => 'Enterprise security.'],
        ],

        // Settings
        'settings' => [
            'favicon' => '/storage/settings/favicon.png',
        ],
    ])->render(), 200, ['Content-Type' => 'text/html']);
});

Any variable you pass to the view is available in the template. There's no whitelist, no injection config — it's standard Laravel view().

Configuration

After publishing, edit config/next-blade.php:

return [
    // Path to your Next.js project
    'frontend_path' => base_path('frontend'),

    // Next.js static export output directory
    'out_dir' => base_path('frontend/out'),

    // Subdirectory inside resources/views/
    'views_subdir' => 'frontend',

    // Directories copied directly to public/
    'public_asset_dirs' => ['_next'],

    // Files in public/ that are never overwritten
    'protected_files' => ['index.php', '.htaccess', 'robots.txt', 'sitemap.xml'],

    // Escape @context, @type etc. in JSON-LD
    'escape_at_symbols' => true,

    // Decode &#x27; and &quot; inside {{ }}
    'decode_html_entities' => true,

    // Register .html files with the Blade engine
    'register_html_extension' => true,
];

What the Compiler Does

NextBlade's compiler is intentionally minimal. It does not inject any variables or replace any HTML elements. It only prepares the HTML so Laravel's Blade engine can compile it without errors:

What Why
@context@@context JSON-LD uses @context, @type, @id which would crash Blade. These get escaped to @@ so Blade ignores them.
&#x27;' inside {{ }} Next.js HTML-encodes quotes. If your Blade expression has {{ $settings[&#x27;key&#x27;] }}, it would fail. The compiler decodes these.
&quot;" inside {!! !!} Same as above for raw expressions.
&#x27;' inside @if(...) Same for directive arguments.

Everything else — variables, directives, loops, conditions, includes — is handled by Laravel's Blade engine natively. You write it, Blade compiles it.

Artisan Commands

Command Description
php artisan next-blade:build Prepare and sync the Next.js export to Laravel
php artisan next-blade:build --npm Run npm run build first, then prepare and sync
php artisan next-blade:build --fresh Clear view cache before building
php artisan next-blade:init Scaffold a new Next.js frontend directory
php artisan next-blade:init --force Overwrite existing frontend directory

Deploying to cPanel / Shared Hosting

Since NextBlade compiles everything at build time, you don't need Node.js on your production server:

  1. Build locally:

    cd frontend && npm run build
  2. Upload the entire Laravel project (including resources/views/frontend/ and public/_next/) to your host.

  3. Run on the server:

    composer install --no-dev
    php artisan config:cache
    php artisan view:cache

No Node.js needed in production. Laravel serves pre-compiled Blade templates with native caching.

Using the Compiler Programmatically

You can use the compiler directly in your own code:

use Tzsmm\NextBlade\HtmlToBladeCompiler;

$compiler = app(HtmlToBladeCompiler::class);

$html = file_get_contents('path/to/page.html');
$prepared = $compiler->compile($html);

file_put_contents('path/to/output.html', $prepared);

Or use the syncer:

use Tzsmm\NextBlade\BuildSyncer;

$syncer = app(BuildSyncer::class);
$result = $syncer->sync();

if ($result['success']) {
    echo "Synced {$result['html_compiled']} pages.";
}

Directory Structure

After building, your Laravel project looks like this:

your-laravel-app/
├── frontend/                          # Next.js source (your React code)
│   ├── src/app/
│   ├── scripts/build-to-laravel.js
│   ├── next.config.ts
│   ├── out/                           # Next.js static export (generated)
│   └── package.json
│
├── resources/views/frontend/          # Blade-ready templates (generated)
│   ├── index.html                     # Home page (Blade-ready)
│   ├── about.html                     # About page (Blade-ready)
│   ├── blog.html
│   └── blog/
│       └── my-post/
│           └── index.html
│
├── public/
│   ├── _next/                         # Next.js JS/CSS chunks
│   ├── favicon.ico
│   └── index.php                      # Laravel entry point (untouched)
│
├── config/next-blade.php              # Package configuration
└── routes/web.php                     # Your routes serving views

FAQ

Q: Does this replace Next.js SSR? No. NextBlade works with Next.js static export (output: 'export'). If you need server-side rendering with React, use a Node.js server. NextBlade is for when you want React's component model for building UI, but want Laravel to handle routing, data, and templating.

Q: Can I use any Blade syntax in my Next.js components? Yes. Any Blade variable ({{ $anything }}), raw output ({!! $html !!}), directive (@if, @foreach, @include, @auth, etc.), or custom Blade directive works. Laravel compiles the HTML files as standard Blade templates.

Q: What about JSON-LD @context and @type? NextBlade automatically escapes these to @@context and @@type so they don't trigger Blade compilation. The output renders correctly as @context in the browser.

Q: Will Next.js HTML-encode my Blade expressions? Next.js may encode ' as &#x27; and " as &quot; in the static export. NextBlade automatically decodes these inside {{ }}, {!! !!}, and @directive() blocks so your PHP syntax stays valid.

Q: Do I need Node.js on my production server? No. Build locally (or in CI), upload the compiled output, and Laravel serves everything natively.

Q: Can I pass any variable to the view? Yes. There's no whitelist or injection config. Any data you pass via view('frontend.index', [...]) is available in the template, exactly like any other Blade view.

Contributing

Pull requests are welcome. For major changes, please open an issue first.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/my-feature)
  3. Commit your changes (git commit -m 'Add my feature')
  4. Push to the branch (git push origin feature/my-feature)
  5. Open a Pull Request

License

MIT

Built with care by TZSMM