Symfony had no official integration with Scalar — the open-source API Reference renderer that makes Swagger UI look its age. Laravel got one back in 2024: scalar/laravel has 231,862 installs on Packagist. Symfony got nothing — not a single package on Packagist, and zero in Scalar's official list of 30+ integrations (Express, FastAPI, NestJS, Spring Boot, Laravel — but no Symfony).
I work with Symfony daily, so I closed the gap: the alex-frolov/scalar-symfony bundle.
What the bundle does
It renders Scalar API Reference from any OpenAPI document. One route, zero coupling to how the spec was generated: a static openapi.yaml, swagger-php, NelmioApiDocBundle, or API Platform all work — because the bundle never parses or proxies the document; the page loads it client-side.
Install and configure in two files:
composer require alex-frolov/scalar-symfony
# config/packages/scalar_symfony.yaml
scalar_symfony:
url: '/openapi.yaml' # your OpenAPI document (required)
path: '/scalar' # route (default: /scalar)
cdn: 'https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.65.1'
configuration:
theme: 'default'
metaData:
title: 'API Reference'
scalar_options: # any Scalar option, passed through as is
darkMode: false
layout: 'modern'
access_control:
mode: public # or 'attribute' + security attribute
That's it — the reference lives at /scalar. The config is serialized XSS-safely (JSON_HEX_TAG/APOS/AMP/QUOT), so even a malicious title can't break out of the <script> context.
Documentation that works as a client
The bundle serves the API reference of the Tender Platform (Symfony 8.1, highload auction API, OpenAPI 3.1 spec). Through Test Request I'm hitting POST /auth/register, filling the JSON body, and pressing Send:
HTTP/1.1 201 Created (794 ms)
{
"company_id": "0c7702c6-9667-4ea3-8caa-df4990522ee7",
"user_id": "86a40d70-5ef9-44fd-882b-8f703e10df7e",
"verification_status": "pending"
}
Real UUIDs, real latency, real backend: the docs page doubles as the tool you test the API with. That's what Swagger UI never delivered.
Quality bar
The first commit was small; the release survived a review against Symfony's official bundle best practices and external hardening passes.
- Functional tests: 16 tests / 46 assertions — routes 200/403/404, config validation errors, XSS-escaping, real SecurityBundle integration;
- Static analysis: PHPStan level max, 0 errors;
-
CI matrix: PHP 8.2/8.3/8.5 × Symfony 6.4/7.2/7.4/8.0, including
--prefer-lowest; -
Config hardening:
attributemode without Symfony Security failscache:clearat compile time with a clear message; - Security docs: SRI (SHA-384), CSP/nonce guidance, self-hosting recipe.
The CI war story: a token that didn't fit
Half the GitHub Actions jobs failed with Your github oauth token for github.com contains invalid characters. setup-php writes the Actions GITHUB_TOKEN (prefixed ghs_) into composer's global auth.json, and Composer 2.8 only accepts ghp_/gho_/github_pat_. The fix: delete auth.json on the runner before composer validate --no-check-publish, keep the token for dependency installation.
Making it official
I opened a proposal in the Scalar organization — Discussion #9920: "Proposal: official Symfony integration (scalar/symfony)" — https://github.com/scalar/scalar/discussions/9920
If you're a Symfony developer who wants modern API docs the way Laravel has them, a reaction on the discussion helps signal maintainer attention.
Start tomorrow: composer require alex-frolov/scalar-symfony → point scalar_symfony.url at any OpenAPI document → import routes and open /scalar.
Aleksander Frolov — Senior/Staff PHP engineer, Symfony/Laravel, highload. frolov.guru · GitHub
Top comments (0)