Add beautiful, interactive CLI forms to your Artisan commands using the laravel/prompts package. Covers text input, selection, search, autocomplete, task logging, streaming output, and more.
Laravel Prompts is a PHP package for adding beautiful and user-friendly forms to your command-line applications. It provides browser-like features such as placeholder text, validation, and styled output.Laravel Prompts is ideal for accepting user input inside Artisan console commands, but it can be used in any command-line PHP project.
Laravel Prompts supports macOS, Linux, and Windows with WSL. In unsupported environments it falls back automatically to simpler input methods.
text() prompts the user for a string and returns the value.
use function Laravel\Prompts\text;$name = text('What is your name?');
Add a placeholder, default value, and hint:
$name = text( label: 'What is your name?', placeholder: 'E.g. Taylor Otwell', default: $user?->name, hint: 'This will be displayed on your profile.');
Make the field required and customize the error message:
$name = text( label: 'What is your name?', required: 'Your name is required.');
Add custom validation logic with a closure:
$name = text( label: 'What is your name?', validate: fn (string $value) => match (true) { strlen($value) < 3 => 'The name must be at least 3 characters.', strlen($value) > 255 => 'The name must not exceed 255 characters.', default => null });
You can also pass Laravel validation rules as an array:
$name = text( label: 'What is your name?', validate: ['name' => 'required|max:255|unique:users']);
number() accepts a numeric value. The user can also use arrow keys to increase or decrease the number.
use function Laravel\Prompts\number;$copies = number( label: 'How many copies would you like?', default: 1, validate: ['copies' => 'required|integer|min:1|max:100']);
password() behaves like text() but masks the typed characters.
use function Laravel\Prompts\password;$password = password( label: 'What is your password?', placeholder: 'password', hint: 'Minimum 8 characters.', validate: fn (string $value) => strlen($value) < 8 ? 'The password must be at least 8 characters.' : null);
confirm() asks a yes/no question and returns true or false.
use function Laravel\Prompts\confirm;$confirmed = confirm('Do you accept the terms?');
Customize the default value and button labels:
$confirmed = confirm( label: 'Do you accept the terms?', default: false, yes: 'I accept', no: 'I decline', hint: 'The terms must be accepted to continue.');
select() presents a list and returns the chosen value.
use function Laravel\Prompts\select;$role = select( label: 'What role should the user have?', options: ['Member', 'Contributor', 'Owner'], default: 'Owner');
Use an associative array to return a key instead of a display label:
$role = select( label: 'What role should the user have?', options: [ 'member' => 'Member', 'contributor' => 'Contributor', 'owner' => 'Owner', ], default: 'owner');
Control how many options are visible before scrolling with the scroll argument (default is 5).
$role = select( label: 'Which category would you like to assign?', options: Category::pluck('name', 'id'), scroll: 10);
multiselect() lets the user select one or more options. It returns an array.
use function Laravel\Prompts\multiselect;$permissions = multiselect( label: 'What permissions should be assigned?', options: ['Read', 'Create', 'Update', 'Delete'], required: 'At least one permission must be selected.');
search() filters a list in real time as the user types.
use function Laravel\Prompts\search;$userId = search( label: 'Search for the user that should receive the mail', options: fn (string $value) => strlen($value) > 0 ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all() : []);
multisearch() combines dynamic search with multiple selection.
use function Laravel\Prompts\multisearch;$userIds = multisearch( label: 'Search for the users that should receive the mail', options: fn (string $value) => strlen($value) > 0 ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all() : []);
autocomplete() provides inline ghost-text completion. Matching suggestions appear as users type and can be accepted by pressing Tab or the right arrow key. Unlike suggest(), the user is encouraged to choose from the provided options.
use function Laravel\Prompts\autocomplete;$name = autocomplete( label: 'What is your name?', options: ['Taylor', 'Dayle', 'Jess', 'Nuno', 'Tim']);
Add a placeholder, default value, and hint:
$name = autocomplete( label: 'What is your name?', options: ['Taylor', 'Dayle', 'Jess', 'Nuno', 'Tim'], placeholder: 'E.g. Taylor', default: $user?->name, hint: 'Use Tab to accept, up/down to cycle.');
Pass a closure to generate options dynamically based on what the user has typed:
Every prompt function accepts a validate argument for custom validation logic. Return an error message string on failure, or null on success.
$name = text( label: 'What is your name?', validate: fn (string $value) => match (true) { strlen($value) < 3 => 'The name must be at least 3 characters.', strlen($value) > 255 => 'The name must not exceed 255 characters.', default => null });
Use the transform argument to modify the input before validation runs.
$name = text( label: 'What is your name?', transform: fn (string $value) => trim($value), validate: fn (string $value) => match (true) { strlen($value) === 0 => 'The name must not be empty.', default => null });
form() groups multiple prompts so the user can go back and edit answers before submitting. If the user cancels, all prompts exit together.
use function Laravel\Prompts\form;$responses = form() ->text('What is your name?', required: true, name: 'name') ->password('What is your password?', validate: ['password' => 'min:8'], name: 'password') ->confirm('Do you accept the terms?') ->submit();$name = $responses['name'];$password = $responses['password'];$confirmed = $responses[2];
Display styled messages without prompting for input.
use function Laravel\Prompts\note;use function Laravel\Prompts\info;use function Laravel\Prompts\warning;use function Laravel\Prompts\error;use function Laravel\Prompts\alert;note('Prepare for launch.');info('User created successfully.');warning('This action cannot be undone.');error('Something went wrong.');alert('Critical failure detected!');
progress() displays a progress bar while iterating over items.
use function Laravel\Prompts\progress;$users = progress( label: 'Updating users', steps: User::all(), callback: fn ($user) => $this->performTask($user), hint: 'This may take some time.');
For manual control, use the start/advance/finish API.
task() displays a labeled task with a spinner and a scrolling live output area while a given callback is executing. It is ideal for wrapping long-running processes such as dependency installation or deployment scripts, providing real-time visibility into what is happening.
use function Laravel\Prompts\task;task( label: 'Installing dependencies', callback: function ($logger) { // Long-running process... });
The callback receives a Logger instance that you can use to display log lines and status messages in real time.
task() requires the pcntl PHP extension to animate the spinner. Without it, a static version of the task will appear instead.
The label method updates the task’s label while it is running, and subLabel displays a dim line beneath it for ephemeral status messages. Pass an empty string to clear the sub-label:
For processes that produce output incrementally — such as AI-generated responses — the partial method streams text word-by-word or chunk-by-chunk. Call commitPartial when the stream is complete:
task( label: 'Generating response...', callback: function ($logger) { foreach ($words as $word) { $logger->partial($word . ' '); } $logger->commitPartial(); });
By default the task displays up to 10 lines of scrolling output. Customize this with the limit argument. To keep the status messages on screen after the task finishes, pass keepSummary: true:
stream() displays text that streams into the terminal incrementally — ideal for AI-generated content or any chunked output.
use function Laravel\Prompts\stream;$stream = stream();foreach ($words as $word) { $stream->append($word . ' '); usleep(25_000); // Simulate delay between chunks...}$stream->close();
The append method adds text to the stream with a gradual fade-in effect. Call close when all content has been streamed to finalize the output and restore the cursor.
Terminal width: Labels, options, and validation messages that exceed the terminal column width are automatically truncated. Aim for a maximum of 74 characters to safely support 80-column terminals.Terminal height: For prompts that accept the scroll argument, the configured value is automatically reduced to fit the terminal height, including space for a validation message.
In environments that don’t support the advanced rendering (such as Windows without WSL), Laravel Prompts automatically falls back to simpler input methods compatible with the host terminal. No code changes are required.
use Laravel\Prompts\Prompt;Prompt::fake(['Taylor', true]);$name = text('What is your name?');$confirmed = confirm('Do you accept the terms?');Prompt::assertOutputContains('What is your name?');
When using Laravel’s Artisan test helpers, you can also assert against informational output functions:
// Pesttest('report generation', function () { $this->artisan('report:generate') ->expectsPromptsInfo('Welcome to the application!') ->expectsPromptsWarning('This action cannot be undone') ->expectsPromptsError('Something went wrong') ->expectsPromptsAlert('Important notice!') ->expectsPromptsTable( headers: ['Name', 'Email'], rows: [ ['Taylor Otwell', '[email protected]'], ] ) ->assertExitCode(0);});
// PHPUnitpublic function test_report_generation(): void{ $this->artisan('report:generate') ->expectsPromptsInfo('Welcome to the application!') ->expectsPromptsWarning('This action cannot be undone') ->expectsPromptsTable( headers: ['Name', 'Email'], rows: [['Taylor Otwell', '[email protected]']] ) ->assertExitCode(0);}