Reprise is a Symfony bundle (it ships no Laravel service provider or facade), so "getting started" for a Laravel developer means one of two things: you're adding it to a Symfony app in a mixed estate, or you're evaluating it as the reference implementation for what laravel-vite-plugin + Vite facade do on the Laravel side.
Minimal path in a Symfony app:
composer require symfony/reprise and accept the Flex recipe — config lands in config/packages/reprise.yaml, assets are emitted to public/build.manifest.json (prod) and entrypoints.json (dev), which is the same artifact pair laravel-vite-plugin produces.*_entry_script_tags / *_entry_link_tags equivalents — check the bundle's Twig extension for exact names in your installed version).php bin/console list reprise to see them; they are the analogue of npm run dev / npm run build with the manifest handled for you).First real use case: replace hand-maintained <script> tags with a single entry per page and let the package resolve hashed filenames, preloads and the dev-server URL. If you only ever need that, you're done in an afternoon.
Entry-per-page, config-driven. Define entries once in reprise.yaml (or the bundle's config tree) and render per template. This is the same mental model as @vite(['resources/js/app.js']) in Blade — one call site, no hashed filenames in templates.
Dev/prod parity without branching. entrypoints.json is served in dev (with the dev-server origin), manifest.json in prod. Keep one template path for both; do not {% if app.environment == 'dev' %} around your tags. In 1.3.0 the dev-server origin is now advertised correctly for wildcard and IPv6 hosts, and Rsbuild honours devServerOrigin, so containerised/reverse-proxied setups can set one externally reachable origin and stop hand-editing host strings.
Preload headers as a response concern. The TagRenderer can emit Link: rel=modulepreload headers alongside the tags. The 1.3.0 fix makes the header follow the final rendered tag (and lets CSP nonces reach both), so the pattern is: let the bundle generate tags, then attach headers from the same render pass via a kernel response listener — never construct preload headers independently of the tag output, or they drift.
CSP nonce plumbing. If you run a strict CSP (or NelmioSecurityBundle's CSP layer), inject the nonce once per request and let TagRenderer thread it onto modulepreload links and their headers. This is the single biggest reason to adopt the bundle over hand-rolled tags — a manual modulepreload list is the classic thing that silently breaks under strict-dynamic.
Stimulus/UX controller discovery. Controllers are auto-registered; 1.3.0 adds .jsx/.tsx local controllers and picks up controller changes in dev without a restart. Pattern: keep controllers in one directory, name them *_controller.js|jsx|tsx, and let discovery do the wiring — same shape as Laravel's import.meta.glob convention.
Atomic emit for deploys. entrypoints.json and manifest.json are now written atomically (single pipeline). That means a request that lands mid-build never sees a truncated manifest — useful if you build in-place on a shared host rather than in a release directory. It does not mean the manifest is consistent with the emitted hashed files; still deploy assets and manifest together.
Cross-stack tip. The manifest is plain Vite format, so if you have a Symfony admin app and a Laravel app sharing one asset build, Laravel's Vite facade can read the same manifest.json — set Vite::useManifestFilename()/build path to the shared directory. Don't bother for a single app; it's only worth it for genuinely shared builds.
publicPath and manifestKeyPrefix. Existing configs keep working, but if you concatenate your own URLs from these values you may now get // or an unexpected leading segment. Also check manifest keys: with manifestKeyPrefix: 'build' the keys become build/… with a guaranteed trailing slash — code doing exact string matches on manifest keys will need updating.joinUrl was removed from the Format helpers. Any extension or template that imported it must inline the join logic. Unused in most apps, breaking if you wrote a custom tag renderer.ViteOutputChunk TS type removed from the Vite integration. If you typed a custom plugin against it, switch to the bundler's own types. Purely a build-time typing change.rsbuild build --watch is now treated as a build, not a dev session: you get manifest.json rather than dev-server output. If CI or a local watch loop was relying on the old behaviour to get a dev-ish asset graph, it will now emit production assets.entrypoints.json is no longer cached. Previously a failed/missing build could poison the cache and survive a successful rebuild; now the first request after the file appears will resolve correctly. If you were compensating with a manual cache:clear step in deploy, drop it — but keep it if you cache the rendered HTML.public/build, expect it to no longer trigger the package's own rebuild. Don't run two watchers on the same output dir.APP_ENV (dev manifest vs prod manifest), publicPath, manifestKeyPrefix (both now trailing-slashed), then whether the entry name in config matches the key in manifest.json. The generated JSON is the source of truth; read it before reading the template.manifest.json for writes may miss the event; watch the directory, not the file, if you have such a tool.joinUrl/type removals if you had custom code.How can I help you explore Laravel packages today?