A new generation of terminal tools (AI coding assistants, log viewers, interactive
dashboards, file managers, etc.) share something the Console component
was never meant to do: render a full-screen interface that redraws in place
and reacts to every keystroke. That’s exactly what the new Tui component,
included in Symfony 8.1, was built for.
We already explored it in depth when we introduced the Tui component, so this
post is a short tour to celebrate that it’s now part of a Symfony release. In a
nutshell, Tui complements the Console component rather than replacing it:
Console stays focused on commands, arguments and output, while Tui takes over
everything related to rich terminal interaction, widgets, layouts, styling, input
handling and real-time rendering. You can even run a full Tui from inside a
console command and fall back to regular Console output when it stops.
Creating an application is straightforward. Instantiate Tui, add some widgets,
react to their events and run the event loop:
use SymfonyComponentTuiEventInputEvent;
use SymfonyComponentTuiInputKeybindings;
use SymfonyComponentTuiTui;
use SymfonyComponentTuiWidgetInputWidget;
use SymfonyComponentTuiWidgetTextWidget;
$tui = new Tui();
$input = new InputWidget();
$input->setPrompt('Name: ');
// widgets are event-driven: react to submit, cancel, change, selection, etc.
$input->onSubmit(fn () => $tui->stop());
$tui->add(new TextWidget('Enter your name and press Enter:'));
$tui->add($input);
$tui->setFocus($input);
// intercept raw keystrokes (here, quit on Ctrl+C)
$keys = new Keybindings(['quit' => ['ctrl+c']]);
$tui->addListener(function (InputEvent $event) use ($tui, $keys) {
if ($keys->matches($event->getData(), 'quit')) {
$tui->stop();
}
});
$tui->run(); // blocks until stop() is called; then the terminal is restored
echo 'Hello, '.$input->getValue()."!n";
Beyond the basics, Tui ships with a complete widget toolkit, so you never draw
the terminal by hand: single and multi-line text editors (with undo/redo, and tab
autocomplete), selectable lists, settings panels, tabs, animated spinners,
progress bars, large FIGlet ASCII-art text and even a Markdown widget that renders
syntax-highlighted code, perfect for displaying AI assistant responses.
Every widget is styled with a CSS-like engine. The simplest way is to apply
an immutable Style value object directly to a widget:
use SymfonyComponentTuiStyleBorder;
use SymfonyComponentTuiStylePadding;
use SymfonyComponentTuiStyleStyle;
$panel->setStyle(new Style(
color: 'cyan',
bold: true,
padding: Padding::all(1),
border: Border::all(1, 'rounded', 'cyan'),
));
For larger applications you can move those rules into stylesheets with
selectors, a cascade and :focus states, or reach for familiar Tailwind-like
utility classes such as p-2, bg-slate-800 and border-rounded.
Tui is also fully non-blocking. It runs on PHP Fibers and the Revolt event
loop, so animations keep playing and the interface stays responsive while
background work runs. A tick callback gives you a per-frame hook to poll async
results and refresh widgets:
use SymfonyComponentTuiEventTickEvent;
$tui->onTick(function (TickEvent $event) use ($future, $loader, $tui) {
if ($future->isComplete()) {
$loader->stop();
return false; // idle: stop ticking and save CPU
}
return true; // busy: keep ticking at high frequency
});
That same loop, combined with getDeltaTime() for time-based updates, is
smooth enough to power long-running operations, live dashboards and even
real-time terminal games. Head over to the Tui documentation to discover all
the widgets, the full styling system and the event and focus management APIs.
This is the last blog post in the New in Symfony 8.1 series. We hope you
enjoyed reading it and that you upgrade your Symfony applications soon. Meanwhile,
work on Symfony 8.2 has already begun in preparation for its release in November 2026.



