skip to content

In Livewire 4, what does php artisan make:livewire create by default, and how do the sfc, mfc and class component formats differ?

level: juniorimportance: should knowfreq 50%

answer

  1. one file, PHP above Blade
  2. lightning-bolt prefix on the filename
  3. --mfc builds a directory
  4. --class restores the v3 layout
  5. make_command.type in config/livewire.php

basics

~20 s

In Livewire 4, make:livewire creates a single-file component by default: one ⚡-prefixed .blade.php file holding an anonymous component class above its Blade markup. --mfc splits it into a directory of files; --class makes the v3-style class plus separate view.

solid answer

~30 s

Livewire 4 changed the default of `php artisan make:livewire`: `make:livewire stock.level` now writes `resources/views/components/stock/⚡level.blade.php`, a **single-file component** whose `<?php new class extends Component { ... }; ?>` block sits above the Blade markup. `--mfc` creates a **multi-file** directory (`level.php`, `level.blade.php`, plus optional `.js`, `.css` and a Pest test with `--test`). `--class` produces the **v3-style** pair, `app/Livewire/Stock/Level.php` with a `render()` that returns `view('livewire.stock.level')`. The default lives in `make_command.type` (`sfc`) and `make_command.emoji` (`true`) in `config/livewire.php`. Whatever the format, the component name stays `stock.level`, so tags and routes do not change, and `php artisan livewire:convert` moves a component between single-file and multi-file.

code

bash · 4 lines
bash
php artisan make:livewire stock.level
php artisan make:livewire stock.level --mfc --test
php artisan make:livewire StockLevel --class
php artisan livewire:convert stock.level --mfc

go deeper

for a junior

Recall the default: one ⚡-prefixed Blade file with the class on top. Know the three flags --sfc, --mfc and --class and what each writes.

for a middle

Explain how the component name is derived independently of the format, why render() is optional in single-file components, and how $this->view() replaces view('livewire.x').

for a senior

Set a team convention: which format for which component, the make_command config, and when livewire:convert is safe given test files and existing tags and routes.

for a principal

Weigh colocated single-file components against class-based ones under app/Livewire for static analysis, review diffs and a Livewire 3 migration path across many teams.

## What changed in Livewire 4 A **Livewire component** is a PHP class whose public properties and methods are driven from a Blade template. Up to Livewire 3, `php artisan make:livewire` always produced two files: a class in `app/Livewire` and a view in `resources/views/livewire`. **Livewire 4** made the **single-file component (SFC)** the default and added a **multi-file component (MFC)** format, while keeping the class-based format for teams that prefer it. Volt, the functional API that used to be the only way to get single-file components, is now optional. ## The three formats side by side | Format | Flag | What gets written for `stock.level` | |---|---|---| | Single-file (default) | `--sfc` | `resources/views/components/stock/⚡level.blade.php` | | Multi-file | `--mfc` | directory `resources/views/components/stock/⚡level/` with `level.php`, `level.blade.php`, optional `level.js`, `level.css`, `level.global.css`, `level.test.php` | | Class-based | `--class` | `app/Livewire/Stock/Level.php` plus `resources/views/livewire/stock/level.blade.php` | Other options of the command: - `--type=sfc|mfc|class` sets the format explicitly. - `--emoji=true|false` overrides the ⚡ filename prefix for one run. - `--test` adds a Pest test file; `--js` and `--css` add those files to a multi-file component. - A `pages::` prefix (`make:livewire pages::stock.dashboard`) writes into `resources/views/pages`, the default namespace for full-page components. ## What a single-file component looks like The PHP part is an **anonymous class** returned by `new class extends Component { ... };`, placed in a `<?php ... ?>` block above the markup. For a warehouse dashboard widget that shows a stock level: ```php <?php use Livewire\Component; new class extends Component { public int $threshold = 10; public function raise(): void { $this->threshold++; } }; ?> <div> Alert below {{ $threshold }} units <button wire:click="raise">+</button> </div> ``` The template must have **exactly one root element**. In debug mode Livewire checks this at mount and throws `MultipleRootElementsDetectedException`. ## Where render() fits - In a **class-based** component, `render()` returns the view explicitly, for example `return view('livewire.stock.level');`. - In a **single-file or multi-file** component you normally **omit `render()`**. Livewire compiles the file and gives the class a protected `view()` method that points at its own template, so if you do want `render()` (to pass extra data or set a layout), you write `return $this->view([...]);`. - `render()` runs on **every** request that re-renders the component, not only the first, so expensive queries do not belong there unless you really want them re-run on each update. A `#[Computed]` property is the usual home for derived data. ## Changing the default and converting The defaults sit in `config/livewire.php`: ```php 'make_command' => [ 'type' => 'sfc', // 'sfc', 'mfc' or 'class' 'emoji' => true, ], ``` Setting `'type' => 'class'` and `'emoji' => false` restores the Livewire 3 behaviour. For an existing component, `php artisan livewire:convert stock.level` auto-detects the format and switches between single-file and multi-file (`--mfc` or `--sfc` forces a direction). Converting a multi-file component back to one file asks for confirmation, because its test file cannot be kept. ## Names do not depend on the format The ⚡ prefix and the directory structure are stripped when Livewire resolves a name, so `resources/views/components/stock/⚡level.blade.php`, `.../⚡level/level.php` and `app/Livewire/Stock/Level.php` are all rendered as `<livewire:stock.level />`. That is what makes conversion safe: no Blade tag or route has to change. The emoji only helps you spot components in a file tree; turning it off does not affect how components are found. ## Choosing a format 1. **Single-file** for most components: state, actions and markup read top to bottom in one place. 2. **Multi-file** when a component grows large or carries real JavaScript and CSS, and you want editor support per file. 3. **Class-based** when migrating a Livewire 3 codebase or when a team wants classes under `app/Livewire` for static analysis and conventions it already has.

  • In a Livewire 4 single-file component, can you leave out render(), and what do you use if you need it?
    Yes. Livewire resolves the template in the same file automatically. If you need `render()` to pass extra data or set a layout or title, return `$this->view([...])`, the protected method Livewire adds to the compiled class, and chain `->layout()` or `->title()` on it. Remember that `render()` runs on every request that re-renders the component.
  • How do you make Livewire 4 generate class-based components by default, as Livewire 3 did?
    Set `make_command.type` to `'class'` in `config/livewire.php`, and usually `make_command.emoji` to `false`. A single run can also pass `--class` or `--type=class`. Existing single-file and multi-file components keep working, because the component name does not depend on the format.
  • Where does Volt fit in a new Livewire 4 project?
    Volt is optional in Livewire 4. Single-file components are built into Livewire itself, so most applications do not need Volt; it remains for developers who prefer its functional, closure-based API. The Livewire starter kit does not require it.

saying these in an interview costs you the question

  • make:livewire always creates app/Livewire/X.php plus a separate view
  • Single-file Livewire components require installing Volt
  • Converting a component to multi-file changes its <livewire:...> tag name
  • The ⚡ prefix is needed for Livewire to discover a component
  • Every Livewire component must declare a render() method
  • render() runs only on the first render, so heavy queries there are fine