The Symfony Ecosystem in 2026: What Packages Are Worth Using

#symfony packages
Sandor Farkas - Founder & Lead Developer at Wolf-Tech

Sandor Farkas

Founder & Lead Developer

Expert in software development and legacy code optimization

Every new Symfony project starts with the same question: which Symfony packages do we actually need? Packagist will happily suggest hundreds. The framework itself ships more than fifty components. Then there are the bundles from SymfonyCasts, Doctrine, API Platform, and a long tail of one-maintainer projects that looked great in 2019 and have not seen a commit since.

We review a lot of Symfony codebases in our code quality consulting work, and dependency sprawl is one of the most consistent findings. Usually no single package is the problem. Nobody asked whether each one was needed, and a composer.json with 90 entries is a maintenance contract nobody signed on purpose.

This post is our opinionated list for 2026. It assumes a production Symfony 7 application (7.4 is the current LTS and the sensible target if you are starting now), PHP 8.3 or newer, and a team that would rather write a small amount of code than adopt a large amount of someone else's.

Start from Symfony's own components before reaching for Symfony packages elsewhere

The first rule of package selection in Symfony is boring: check whether the framework already does it. In practice we keep finding third-party rate limiters, UUID libraries, and HTTP clients sitting next to the Symfony components that replaced them years ago.

A few components that belong in nearly every application and are still underused:

symfony/rate-limiter covers login throttling, API quotas, and abuse protection with a handful of YAML lines and a storage backend you already have (Redis, Doctrine, or the cache pool). Teams still pull in standalone throttling libraries for this. Do not.

symfony/uid gives you UUID v7 and ULID generation with Doctrine types included. If you are starting a new schema, UUID-first primary keys are worth the small storage cost: they let you generate IDs before the insert, merge data across environments without collisions, and stop leaking row counts through sequential integers. ramsey/uuid is a fine library, but you no longer need it as a direct dependency.

symfony/messenger handles async work, retries, failure transports, and scheduling (through symfony/scheduler since 6.3). A surprising number of teams add a separate job queue library on top of Messenger because they did not know about the retry strategy configuration. We covered the operational side in Messenger at scale.

symfony/http-client replaces Guzzle for outbound calls in almost every case. It supports retries, scoped clients per API, mocking in tests, and HTTP/2 out of the box. Guzzle still appears in composer.json because some SDK requires it, which is fine, but do not write new code against it.

symfony/serializer and symfony/validator are the other two we see replaced without cause. More on the serializer below.

Symfony packages that earn a place in almost every production app

After the components, there is a short list of bundles we install on nearly every project and have never regretted.

symfonycasts/reset-password-bundle

Password reset is one of those features that every team thinks it can write in an afternoon and then gets subtly wrong: tokens that never expire, tokens stored in plain text, no throttling on request, user enumeration through different error messages. This bundle handles the token lifecycle correctly, integrates with the maker bundle, and stays out of your way. It is about 30 files. Install it.

symfonycasts/verify-email-bundle

Same maintainer, same reasoning. Signed URLs for email verification, expiry, and no custom crypto in your codebase.

doctrine/doctrine-fixtures-bundle (dev only)

Fixtures are the backbone of realistic test data. Pair the bundle with zenstruck/foundry if you want factory-style object creation in tests. Foundry has become the de facto standard for Symfony test factories and pairs well with zenstruck/browser for functional tests. Keep both in require-dev.

nelmio/security-bundle

Security headers (CSP, HSTS, X-Frame-Options, referrer policy) are tedious to configure by hand and easy to get wrong. NelmioSecurityBundle turns them into config. You can write a listener that does the same thing in an hour, and some teams prefer that. We generally take the bundle because CSP nonces and report-only mode are the parts people skip when they roll their own.

scheb/2fa-bundle

If your application has an admin area or handles customer data, two-factor authentication is not optional in 2026. Scheb's bundle supports TOTP, email codes, and backup codes, and it slots into Symfony's security system rather than replacing it. It is the standard choice and we have not seen a reason to look elsewhere.

symfony/monolog-bundle

Still the logging default. Configure a JSON formatter for production so your log aggregator can parse it, and add a fingers-crossed handler so debug output only appears when a request errors.

sentry/sentry-symfony (or your error tracker's equivalent)

Error tracking is a production requirement. Whichever provider you use, install its official bundle rather than wiring the SDK manually; the bundle captures Messenger failures and console command errors that a manual integration usually misses.

APIs: API Platform for CRUD-heavy, plain Symfony for event-driven

The API layer is where package decisions have the biggest long-term cost, so it deserves its own section.

For applications whose API is mostly resources with standard operations (list, read, create, update, delete, filter, paginate), API Platform is still the best choice we know of. You define the resource once and get OpenAPI docs, JSON-LD or plain JSON, pagination, filtering, validation integration, and security expressions. The API Platform post goes into the setup and where the pagination and N+1 defenses live.

The cost is conceptual. API Platform has its own vocabulary (state providers, state processors, resource metadata) and its own opinions about how your domain maps to HTTP. If your API is a thin layer over a well-formed Doctrine model, that mapping is nearly free. If your API is mostly commands ("start the export", "approve the invoice", "recalculate pricing") the resource model starts to fight you, and you end up writing custom operations for everything anyway.

For that second kind of application, plain Symfony is enough. Controllers with #[MapRequestPayload] for input DTOs, the Validator for constraints, the Serializer for output, and Messenger for anything that should run asynchronously. That is four components you already have and zero new mental models. Add nelmio/api-doc-bundle if you need generated OpenAPI documentation, or write the schema first and validate against it; we described the schema-first approach in our OpenAPI contract testing guide.

What we advise against is running both patterns in one application. Pick the one that matches the majority of your endpoints and accept some awkwardness on the minority.

The packages we tell teams to reconsider

None of these are bad software. Each has a real use case. The problem is that they get installed by default when the default should be "not yet".

JMS Serializer

JMS was the serializer of choice before Symfony's own component matured. In 2026 the Symfony Serializer handles nested objects, groups, naming strategies, discriminator maps for polymorphism, and circular references. JMS still has better handling of a few edge cases (exclusion policies, versioned properties), and if you are already deep in it there is no urgent reason to migrate. But for a new application, adding JMS means two serializers in the container with different annotation conventions, and every new developer has to learn which one is used where. Stay with the component unless you hit a concrete limitation.

Sonata Admin and EasyAdmin

Admin bundles are the most contested choice on this list, so here is the nuance. EasyAdmin is a good fit for straightforward CRUD screens over Doctrine entities with a small team that will not customize them much. Sonata is more powerful and more complex, and its configuration surface is large enough that customizing it often takes longer than writing the screen from scratch.

The pattern we see repeatedly: a team installs an admin bundle in week one, spends weeks two through six bending it to non-standard workflows (approval steps, bulk actions with side effects, multi-entity forms), and ends up with a system that nobody outside the original author can modify. A plain Twig interface, or a small HTMX-based admin, is usually faster past the first month and far easier to reason about in a code review.

If your admin requirements really are standard, EasyAdmin is fine. If you find yourself reading its internals to override a form field, that is the signal to stop.

FOSUserBundle and FOSRestBundle

Both still show up in codebases we audit. FOSUserBundle is effectively retired; the maker bundle plus the SymfonyCasts bundles above cover the same ground with less magic. FOSRestBundle solved problems that #[MapRequestPayload], the Serializer, and API Platform now solve natively. If you see either in a project dated after 2022, treat it as a modernization item.

Custom "core" bundles from a previous project

Teams carry a company-internal bundle from project to project because it once saved time. Three projects later it contains abstractions for problems the current project does not have. Ask what it provides that Symfony 7 does not, and vendor in only the pieces that survive that question.

How to evaluate a package before you add it

The specific list of Symfony packages above will age. The evaluation habit is what matters. Before any composer require on a production project, we run through a few questions.

Is there a Symfony component or a Flex recipe that already covers this? Check the Symfony Flex recipes repository; if a package has a recipe, it is at least maintained enough for someone to write one.

When was the last release, and does it declare support for the Symfony and PHP versions you run? A package with open issues titled "Symfony 7 support?" and no maintainer reply is a future blocker on your next upgrade. This is the single biggest source of pain in legacy modernization work: the upgrade itself is fine, but four abandoned bundles pin you to an old framework version.

How much of the package would you use? If you need one helper class out of forty, copy the class (respecting the license) and drop the dependency.

Who else depends on it? Check the dependents count on Packagist. A package used by a handful of projects is a package you may end up maintaining yourself.

Can you explain to a new team member why it is there? If the honest answer is "the tutorial used it", remove it.

A minimal composer.json for a production Symfony 7 application

This is roughly what we start from. Adjust the API section based on the decision above, and swap the error tracker for yours.

{
    "type": "project",
    "require": {
        "php": ">=8.3",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "doctrine/dbal": "^4",
        "doctrine/doctrine-bundle": "^2.13",
        "doctrine/doctrine-migrations-bundle": "^3.4",
        "doctrine/orm": "^3.3",
        "nelmio/security-bundle": "^3",
        "scheb/2fa-bundle": "^7",
        "scheb/2fa-totp": "^7",
        "sentry/sentry-symfony": "^5",
        "symfony/console": "7.4.*",
        "symfony/dotenv": "7.4.*",
        "symfony/flex": "^2",
        "symfony/framework-bundle": "7.4.*",
        "symfony/http-client": "7.4.*",
        "symfony/mailer": "7.4.*",
        "symfony/messenger": "7.4.*",
        "symfony/monolog-bundle": "^3.10",
        "symfony/rate-limiter": "7.4.*",
        "symfony/runtime": "7.4.*",
        "symfony/scheduler": "7.4.*",
        "symfony/security-bundle": "7.4.*",
        "symfony/serializer": "7.4.*",
        "symfony/twig-bundle": "7.4.*",
        "symfony/uid": "7.4.*",
        "symfony/validator": "7.4.*",
        "symfony/yaml": "7.4.*",
        "symfonycasts/reset-password-bundle": "^1.23",
        "symfonycasts/verify-email-bundle": "^1.17"
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^4",
        "phpstan/phpstan": "^2",
        "phpstan/phpstan-symfony": "^2",
        "phpunit/phpunit": "^11",
        "symfony/browser-kit": "7.4.*",
        "symfony/debug-bundle": "7.4.*",
        "symfony/maker-bundle": "^1.60",
        "symfony/phpunit-bridge": "7.4.*",
        "symfony/stopwatch": "7.4.*",
        "symfony/web-profiler-bundle": "7.4.*",
        "zenstruck/browser": "^1.9",
        "zenstruck/foundry": "^2"
    },
    "config": {
        "allow-plugins": {
            "php-http/discovery": true,
            "symfony/flex": true,
            "symfony/runtime": true
        },
        "sort-packages": true
    },
    "extra": {
        "symfony": {
            "allow-contrib": false,
            "require": "7.4.*"
        }
    }
}

A few notes on the choices. Version constraints are indicative; run composer outdated after install and pin to what resolves. PHPStan at level 6 or higher from day one is cheaper than adding it at level 0 a year later and climbing. allow-contrib is false so that Flex only auto-configures packages with recipes from the main repository, which keeps surprises out of your config directory. There is no admin bundle, no JMS, and no Guzzle. Add API Platform (api-platform/symfony) if the API section pointed you that way.

That list has around thirty production dependencies, most of them Symfony components that upgrade together. That is the shape you want. One framework version to bump, and a handful of well-maintained bundles around it.

When the package question is really an architecture question

Sometimes the debate about which bundle to install is a proxy for a decision the team has not made yet: whether the admin is a product surface or an internal tool, whether the API is a resource API or a command API, whether the project will still be maintained in three years by people who were not there at the start. Packages are easy to add and hard to remove, so those questions are worth answering before the first composer require.

If you are starting a Symfony application and want a second opinion on the dependency list, or you have inherited one with a composer.json that nobody can explain, we do this kind of review as part of our custom software development and code audit work. Write to hello@wolf-tech.io or find out more at wolf-tech.io.