In Laravel 13, how does `laravel new` differ from `composer create-project laravel/laravel` when you start a new application?
answer
- one command wraps the other
- the skeleton's own Composer scripts
- .env copy, key:generate, SQLite file, migrate
- installer layers on Pest, npm build, Boost
- plain create-project keeps PHPUnit, no kit
basics
~20 scomposer create-project laravel/laravel copies the bare skeleton and runs its Composer scripts (.env, app key, SQLite file, migrations). laravel new wraps that step and adds prompts, an optional starter kit, Pest by default, a front-end build and Boost.
solid answer
~40 sBoth start from the `laravel/laravel` skeleton. `composer create-project laravel/laravel vet-clinic` downloads it, installs its dependencies and runs the skeleton's scripts: `post-root-package-install` copies `.env.example` to `.env`, and `post-create-project-cmd` runs `key:generate`, creates `database/database.sqlite` and runs `migrate --graceful`. You get the bare skeleton with PHPUnit and SQLite, and no front-end build. `laravel new vet-clinic` is the installer CLI (`laravel/installer`): it checks PHP extensions, asks whether you want a starter kit, runs `create-project` itself, configures the database, swaps PHPUnit for Pest unless you pass `--phpunit`, installs and builds the npm dependencies, adds Boost, and prints the next step, usually `composer run dev`. The installer suits a workstation; plain `create-project` suits CI or a container with no global installer.
code
bash · 8 lines# Plain Composer: bare skeleton, PHPUnit, SQLite, no front-end build
composer create-project laravel/laravel vet-clinic
# Installer: prompts, Pest by default, npm build, Boost
composer global require laravel/installer
laravel new vet-clinic
cd vet-clinic
composer run devgo deeper
Know both commands by name, and that each gives you a .env file, an app key and a migrated SQLite database. Be able to say the installer is a separate tool installed globally with Composer.
Explain where each setup step comes from: the skeleton's post-root-package-install and post-create-project-cmd scripts versus the installer running the same steps itself, plus the Pest swap and the npm build.
Pick the right tool per context: the installer for laptops, create-project with a version constraint for CI images and templates, and a PHP version check first because both resolve the newest skeleton your PHP allows.
For many teams, decide whether new services come from the installer's defaults or from an internal template built on create-project, and who owns keeping that template on the current major.
## What both commands start from Every new Laravel 13 application begins as a copy of **`laravel/laravel`**, the *skeleton*: a small Composer project that requires `laravel/framework` (the framework itself lives in `vendor/`) and ships `bootstrap/app.php`, `routes/`, `config/`, three default migrations, a `composer.json` with scripts and a `package.json` for Vite. The two commands differ in **what happens around that copy**, not in the copy itself. ## `composer create-project`: the skeleton and its scripts `composer create-project laravel/laravel vet-clinic` asks Composer to download the newest skeleton release your PHP can install, run `composer install` inside it, and then fire the **Composer scripts** the skeleton declares in its `composer.json`. In the Laravel 13 skeleton those scripts do the setup: 1. `post-root-package-install` copies `.env.example` to `.env` if no `.env` exists. 2. `post-create-project-cmd` runs `php artisan key:generate`, which writes `APP_KEY` into `.env`. 3. The same script touches `database/database.sqlite`, because the skeleton's `.env.example` sets `DB_CONNECTION=sqlite`. 4. It finally runs `php artisan migrate --graceful`, creating tables that include `users`, `sessions`, `cache` and `jobs`; `--graceful` returns a success exit code even if migrating fails. What you get is deliberately bare: **PHPUnit** as the test runner (`phpunit/phpunit` in `require-dev`), no Pest, no authentication pages, and no `node_modules` or built assets. ## `laravel new`: the installer on top `laravel new vet-clinic` runs the **Laravel installer**, a separate global CLI (`composer global require laravel/installer`, or bundled with Herd and the php.new script). In installer v5.32 it: - checks that the `ctype`, `filter`, `hash`, `mbstring`, `openssl`, `session` and `tokenizer` extensions are loaded, and stops with an error listing any that are missing; - asks whether you want a **starter kit** (default: no) and, depending on the answer, which front-end stack to build on; - runs `composer create-project laravel/laravel` with `--no-scripts` (or creates the project from the chosen kit's repository), then copies `.env` and runs `key:generate` itself; - writes `DB_CONNECTION` for the chosen driver (SQLite unless you pass `--database`), creates the SQLite file and migrates; - removes PHPUnit and installs **Pest** unless you pass `--phpunit`; - runs `npm install` and `npm run build` (or pnpm, Bun or Yarn), and installs **Laravel Boost** unless you pass `--no-boost`; - prints the next steps, which end in `composer run dev` unless the folder is already served by Herd or Valet. ## Side by side | Concern | `composer create-project laravel/laravel` | `laravel new` | |---|---|---| | Needs a global tool | Composer only | Composer plus the installer | | `.env`, `APP_KEY`, SQLite file, migrations | Yes, via skeleton scripts | Yes, run by the installer | | Test runner | PHPUnit | Pest unless `--phpunit` | | Starter kit | None | Optional, prompted | | Front-end dependencies built | No | Yes unless `--no-node` | | Choosing a version | A constraint argument, e.g. `"13.*"` | Newest release your PHP allows, or `--dev` for the development branch | ## Which one to reach for - On a developer laptop the installer is the documented path: one command gives a working app, tests and assets. - In a Dockerfile, a CI job or a scripted template, `create-project` avoids installing a global CLI and lets you pin the skeleton version. - Both resolve the **newest skeleton your PHP can install**, so on an old PHP either one can quietly give you an older major. Check `php -v` first. - Neither command starts a server: that is `composer run dev`, `php artisan serve`, or a Herd or Valet parked folder. ## A worked example: the clinic app Suppose the team starts a **veterinary-clinic booking app** called `vet-clinic`: - `composer create-project laravel/laravel vet-clinic` leaves a folder with `.env`, a generated `APP_KEY`, `database/database.sqlite` already holding the `users`, `sessions`, `cache` and `jobs` tables, and `tests/Feature/ExampleTest.php` written for PHPUnit. Opening a page that uses `@vite` still needs `npm install` and a build or the Vite dev server. - `laravel new vet-clinic` with default answers produces the same database state, but the tests are Pest files, `node_modules` and `public/build` exist, Boost's agent guidelines are installed, and the closing message tells you to run `composer run dev`. Either folder can be committed and cloned; teammates then install dependencies with Composer and npm rather than re-running either creation command. ## Common misreadings - The installer is not a different framework or a different skeleton; it drives the same `create-project` step. - `create-project` is not "empty": it already produces `.env`, an app key and a migrated SQLite database. - Pest is not part of the skeleton; it arrives only because the installer adds it.
- Why does the Laravel installer pass --no-scripts to create-project for the plain skeleton?It takes over the skeleton's setup steps so it can order them around its own choices. After `create-project ... --no-scripts` it runs `post-root-package-install` and `key:generate` itself, then writes `DB_CONNECTION` for the driver you picked, creates the SQLite file if needed and only then migrates. Letting the skeleton's `post-create-project-cmd` run first would already have migrated SQLite before the installer configured the chosen driver.
- How do you pin the Laravel version with each command?`create-project` takes a version constraint after the directory, for example `composer create-project laravel/laravel vet-clinic "13.*"`. The installer has no version option: it creates the latest release your PHP can install, and `--dev` switches it to the skeleton's development branch. To pin a major from the installer path, check your PHP version or fall back to `create-project` with a constraint.
saying these in an interview costs you the question
- laravel new downloads a different, heavier skeleton than create-project
- create-project leaves you without a .env file or an app key
- The Laravel skeleton ships with Pest preinstalled
- Either command also starts a local web server when it finishes
- You must create a MySQL database before either command succeeds