Laravel
Next release
How midcode runs a Laravel app, edits its Blade views on the canvas, and handles the React, Vue and Livewire starter kits, without changing a file to do it.
This ships with the next release of midcode. The version you can download today (1.1.2) doesn’t have it yet.
midcode opens a Laravel app and edits its Blade views on the canvas: text, classes, attributes, new elements and their order, written into the .blade.php file. Pages made with Inertia (React or Vue) and with Livewire are edited too. What PHP prints ({{ $title }}) is changed where the value is written, not on the canvas.
There are three layers, and they don’t ship at the same time:
| Layer | What you get | Status |
|---|---|---|
| The app on the canvas | A Laravel app that uses Vite, shown at its APP_URL when that’s a localhost or 127.0.0.1 address: every breakpoint, Preview, comments. Nothing is editable | In 1.1.2 |
| Blade | Blade views edited on the canvas, with or without Vite | Next release |
| Starter kits | Inertia pages in React or Vue, and Livewire and Volt pages | Next release |
At a glance
| Laravel | |
|---|---|
| Detected by | An artisan file at the top of the folder |
| Runs with | PHP’s built-in server on a port midcode picks, with one file of midcode’s loaded first. Vite runs beside it when the app uses Vite |
| Elements are marked by | Blade, as it compiles each view. Inertia pages by midcode’s Vite plugin |
| Editing | Text, classes, attributes and tag; insert, move, duplicate and remove |
| Components | <x-…> and Livewire components are edited in their own view. An instance isn’t selectable |
| Pages | The GET routes of routes/web.php. A new page is a Blade view and a Route::view line |
| Styles | Tailwind 4 classes, or mid: utilities with public/midcode.css |
| Tried with | Laravel 13 on PHP 8.5, and the React, Vue and Livewire starter kits of Laravel 12 |
In the version you can download today
midcode 1.1.2 opens a Laravel app whose package.json has Vite. It starts Vite and shows the site at the APP_URL of your .env (http://localhost:8000 when there’s none).
If nothing answers there five seconds after Vite is up, and
APP_URLis a plainhttpaddress on this Mac, midcode runsphp artisan serveon that host and port in your shell.You can check every breakpoint, use the site in Preview and leave comments. Blade views can’t be edited.
A site on a host name of its own, like the
https://shop.testthat Herd or Valet give it, stays blank in 1.1.2: that version only letslocalhostaddresses into the canvas. The next release shows any host.A Laravel app without Vite isn’t recognised in 1.1.2.
This layer was tried with a stand-in server answering at
APP_URL, not with Herd or Valet themselves.
Everything below is the next release.
How midcode runs it
When it can, midcode serves the app itself instead of running php artisan serve. It starts PHP’s built-in server, the same one artisan serve runs, from your app’s public/ folder:
php -d auto_prepend_file=<midcode's data folder>/injected/blade-prepend.php \
-S 127.0.0.1:<port> server.phpphp is the first one on your login shell’s PATH. server.php is your project’s own if it has one, else the one inside vendor/laravel/framework. The port is a free one midcode picks. Two more things are set for that process only: VIEW_COMPILED_PATH points at a folder in midcode’s data folder, and PHP’s deprecation notices aren’t printed into the page (they still go to the log).
The file loaded first is what gets the marks in:
It loads Composer’s autoloader. When Laravel first asks for its Blade compiler, it defines that class from Laravel’s own source with one line added at the top of
compileString().That line hands each view of your project (never one under
vendor/) to a small server midcode runs on127.0.0.1. It answers with the same source, with adata-mc="file:line:col"attribute on every element. Blade compiles that.The compiled views land in midcode’s folder, which is emptied at every start.
storage/framework/viewsisn’t used, so what Herd orartisan serveshow later has no marks in it.If anything looks unfamiliar (another Blade compiler, no answer in three seconds), Blade compiles the view as it is.
The same file adds one script to the HTML it serves, so the page reloads when a .php, .css or .js file under resources/ changes.
midcode adds no file to your project and changes none to do this. Your app still writes its own logs, sessions and cache to storage/, as it does under any server.
With Vite
When package.json has Vite, midcode starts it with your own Vite config plus its plugin, on a port it picks. Laravel’s Vite plugin chooses the host, as it does for you. The canvas shows midcode’s PHP server once Vite is up and the site answers.
Without Vite
Only the PHP server runs.
When midcode can’t serve it
If there’s no php on your PATH, no server.php, or no public/ folder, midcode falls back to how the app is usually served: it shows APP_URL if something already answers there (Herd, Valet), and otherwise runs php artisan serve. Those pages arrive without Blade’s marks. midcode then looks each element up in your views after the page loads, the way it does for any site a server renders. That path hasn’t been tried with Herd.
What you need
PHP on your Mac, on your shell’s PATH.
The app in a state where it runs:
composer installdone, a.envwith its key, its database reachable. midcode doesn’t run Composer or migrations on a project it opens.For an app with Vite, its
node_modules. midcode offers to install them when they’re missing.
What you can edit
Click any element of a view: the right panel shows its file and line under Code. Double-click a text to change it.
<section class="px-6 py-16">- <h1 class="text-4xl font-semibold">Fresh bread, every morning</h1>+ <h1 class="text-5xl font-semibold">Bread, pastries and coffee</h1> <p class="mt-4 text-neutral-600">{{ $tagline }}</p>What Blade computes is read as “not written here” and never touched:
{{ $x }},{!! $x !!},@if(…),@foreach(…),@csrfand every other directive,@php … @endphp,<?php ?>.{{-- … --}}is a comment.In an attribute, the written part and the PHP part are told apart: in
class="card {{ $active ? 'on' : '' }}"the panel changescardand leaves the rest.:name,@click,x-…,wire:…and@class([…])on a tag are left as they are.
An element drawn inside @foreach is one line of the view, shown many times: editing its classes changes all of them. A tag opened in one branch of an @if and closed in another can’t be moved or removed from the canvas.
Blade components
<x-card> and <livewire:counter /> aren’t selectable as instances, and they have no props panel. What a component draws is marked in its own view (resources/views/components/card.blade.php), so you edit it there by clicking it. What you write between a component’s tags is marked where you wrote it.
Starter kits
Laravel’s own kits were tried, on Laravel 12.
React and Vue, through Inertia
The pages are components in resources/js/pages. midcode’s Vite plugin marks them as Vite builds them, exactly as in a Vite + React or Vue project, and Blade marks resources/views/app.blade.php. On the page that was measured, 161 of 164 elements had a mark. A text or class edit shows through Vite’s hot reload, without the page loading again.
Livewire and Volt
Livewire’s views are Blade, so they’re marked like any view: 162 of 162 elements on the kit’s home page. The marks survive a Livewire round trip, because the Blade it sends back is already marked. On a Volt page, what isn’t marked are Flux’s components: they come from vendor/.
New project in the hub makes any of them (PHP, then Laravel, Laravel + React, Laravel + Vue or Laravel + Livewire) with composer create-project. See New project.
Pages
Pages in the top bar lists the GET routes written out in routes/web.php, and in the route files it brings in with require __DIR__.'/auth.php'.
| Route | Page | File it opens |
|---|---|---|
Route::view('/about', 'about') | /about | resources/views/about.blade.php |
Route::get('/', fn () => view('welcome')) | Home | resources/views/welcome.blade.php |
Route::get('/', fn () => Inertia::render('welcome')) | Home | resources/js/pages/welcome.tsx (or .jsx, .vue) |
Volt::route('/profile', 'settings.profile') | /profile | resources/views/livewire/settings/profile.blade.php |
Route::get('/posts/{post}', [PostController::class, 'show']) | /posts/[post] | None: the view isn’t named in the route |
A route with a parameter is a dynamic page: midcode finds its real addresses from the running site’s sitemap and links (Pages and navigation). You can always type a path such as /pricing in Pages and press Enter.
“New page” in that menu writes a Blade view and the route that shows it, at the end of routes/web.php:
@extends('layouts.app')
@section('content')
<main class="mx-auto max-w-3xl px-6 py-24">
<h1 class="text-4xl font-semibold">About</h1>
<p class="mt-4">A new page, made in midcode.</p>
</main>
@endsection Route::get('/', fn () => view('welcome'));++Route::view('/about', 'about');The view is written like the home page’s: under the same @extends and @section when the home view extends a layout, otherwise as a whole document with a copy of the home view’s <head>. /blog/first is resources/views/blog/first.blade.php, shown by Route::view('/blog/first', 'blog.first'). The class names are written when Tailwind is in your package.json. A new page is a Blade view in an Inertia app too.
“Remove” moves a page’s view to the Trash and takes its Route::view line out with it. Making a page, seeing it on the canvas and removing it were tried on a Laravel app.
Site settings
The globe in the top bar opens Site settings. In a Laravel app the title, the description, the language, search engines, the favicon and the social image are tags in the <head> of a view: the first one that closes a <head>, in alphabetical order of its path. Images are copied into public/.
What Blade computes there is shown as code and isn’t written: a <title>{{ config('app.name', 'Laravel') }}</title> shows in the form as that code, and stays your code’s to set. There’s one form for the whole app. Tried on a Laravel app.
Styles
Tailwind 4 (what Laravel sets up through Vite): midcode writes Tailwind’s classes and Vite compiles them. See Tailwind CSS.
No Tailwind, no Vite: midcode writes
mid:utilities, keeps their CSS inpublic/midcode.css, and adds<link rel="stylesheet" href="/midcode.css">before</head>in every layout view. See Without Tailwind.Vite without Tailwind 4: midcode writes
midcode.cssat the top of the project and says it couldn’t tell where to import it. Import it fromresources/js/app.js.
Limits
Text that PHP prints is edited where the value is written: the controller, a language file, the database. midcode’s own search for a text reads
.json,.js,.tsand.mdfiles, never.phpor.blade.php.Views that come from
vendor/(a package’s, Flux’s) are never marked.Elements move inside one view. Moving one from a partial into the view that includes it is done in the code.
Routes inside a group with a prefix are listed without the prefix, and
Route::resourceisn’t read.The canvas shows the app at
http://127.0.0.1:<port>, not at itsAPP_URL. Anything tied to the host name (a session domain, an OAuth callback) behaves as it would at that address.Not tried: Herd, Valet, Sail, Inertia with Svelte.
The free canvas is for Next.js projects.
“Remove” only knows the
Route::viewline midcode writes. For a page whose route is written another way (Route::getwith a closure or a controller), the view goes to the Trash and the route stays: take that line out yourself.With several views that close a
<head>(a layout for the site and one for sign-in pages), Site settings writes the first by path, which may not be the one your pages use. The note under the form’s images names the file it writes.A MySQL or MariaDB database can’t be opened yet. midcode’s Database finds the connection from the
DB_*values of.env, and opens Postgres and SQLite.
Troubleshooting
“Vite is running, but the site isn’t answering at … (APP_URL in .env).” midcode’s PHP server didn’t come up and nothing else serves the app. Run php -v in a terminal: if it isn’t found, PHP isn’t on your PATH. If it is, open the Dev server log in the top bar to read what PHP said.
Some elements of a Blade view have no file and line. midcode is probably showing the app from another server and finding elements after the page loads, which finds fewer. When midcode serves the app itself, the log has a line starting with $ php -S 127.0.0.1:. If it’s missing, check that php is on your PATH.
A Laravel error page. That’s your app answering: a missing .env, no APP_KEY, a database that isn’t running. Fix it as you would in a browser, then press Reload breakpoints.
To serve it your own way, use Change how it runs on the error card: a command, an address, or both (Any other stack). Your command then replaces midcode’s start altogether: Vite isn’t started for you.