DEMOS / LiveComponent

Inline Editing

Inline editing? Simple. Use LiveComponents to track if you're in "edit" mode, let the user update any fields on your entity, and save through a LiveAction.

Banana 🍌

The Banana 🍌 has 23 votes! Yum!

// ... use statements hidden - click to show
use App\Entity\Food;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\DefaultActionTrait;
use Symfony\UX\LiveComponent\ValidatableComponentTrait;

class InlineEditFood extends AbstractController
    use DefaultActionTrait;
    use ValidatableComponentTrait;

    /** This allows us to have a data-model="" */
    #[LiveProp(writable: ['name'])]
    /** When we validate, we want to also validate the Food object */
    public Food $food;

    /** Tracks whether the component is in "edit" mode or not */
    public bool $isEditing = false;

     * A temporary message to show to the user.
     * This is purposely not a LiveProp: this is a "temporary" value that
     * will only show one time.
    public ?string $flashMessage = null;

    public function activateEditing()
        $this->isEditing = true;

    public function save(EntityManagerInterface $entityManager)
        // if validation fails, this throws an exception & the component re-renders

        $this->isEditing = false;
        $this->flashMessage = 'Saved! Well, not actually because this is a demo (if you refresh, the value will go back).';

        // in a real app, we would save!
        // $entityManager->flush();
<div {{ attributes }}>
    {% if flashMessage %}
        <div class="alert alert-success">{{ flashMessage }}</div>
    {% endif %}

    <div class="d-inline-flex">
        {% if isEditing %}
            {# The form isn't used, but allows the user to hit enter to save. #}
            <form class="row g-3">
                <div class="input-group mb-3 col">
                    {% set error = this.getError('') %}

                    <div class="form-floating">
                            class="form-control form-control-lg{{ error ? ' is-invalid' }}"
                        <label for="food_name">Food name</label>

                        class="btn btn-outline btn-outline-{{ error ? 'danger' : 'secondary' }}"

                    {% if error %}
                        <div class="invalid-feedback">{{ error.message }}</div>
                    {% endif %}
                <div class="form-text">Clear the field to trigger validation!</div>
        {% else %}
            <h2>{{ }}</h2>
                class="btn btn-link"
                title="Click to edit!"
                <twig:Icon name="pencil" />
        {% endif %}

        The <strong>{{ }}</strong> has {{ food.votes }} votes! Yum!