tzsmm / next-blade
Compile Next.js static HTML exports into Laravel Blade templates with dynamic variable injection, favicon replacement, and native view caching.
Requires
- php: ^8.1
- illuminate/console: ^10.0||^11.0||^12.0||^13.0
- illuminate/support: ^10.0||^11.0||^12.0||^13.0
- illuminate/view: ^10.0||^11.0||^12.0||^13.0
This package is auto-updated.
Last update: 2026-08-21 09:38:02 UTC
README
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) │
└─────────────────────┘
- You build your frontend with Next.js using
output: 'export'(static HTML export). - You write any Blade syntax directly in your Next.js components —
{{ $title }},{!! $content !!},@if,@foreach,@include, etc. - NextBlade prepares the exported HTML for Blade (escapes JSON-LD
@symbols, decodes HTML entities) and syncs files toresources/views/frontend/. - 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,@idwon'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 commands —
next-blade:buildto sync,next-blade:initto scaffold a new frontend - Node.js script included —
npm run buildauto-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.tsxandlayout.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 ' and " 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. |
' → ' inside {{ }} |
Next.js HTML-encodes quotes. If your Blade expression has {{ $settings['key'] }}, it would fail. The compiler decodes these. |
" → " inside {!! !!} |
Same as above for raw expressions. |
' → ' 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:
-
Build locally:
cd frontend && npm run build
-
Upload the entire Laravel project (including
resources/views/frontend/andpublic/_next/) to your host. -
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 ' and " as " 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.
- Fork the repository
- Create your feature branch (
git checkout -b feature/my-feature) - Commit your changes (
git commit -m 'Add my feature') - Push to the branch (
git push origin feature/my-feature) - Open a Pull Request
License
Built with care by TZSMM