Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Env To Js Symfony Bundle Laravel Package

elfennol/env-to-js-symfony-bundle

View on GitHub
Deep Wiki
Context7

env-to-js Symfony Bundle

Inject Symfony .env variables into the browser at runtime, via <script type="application/json"> tags read by the @elfennol/env-to-js TypeScript package.

BEFORE  build(staging) → app.staging.js   ← rebuild required for each environment
        build(prod)    → app.prod.js

AFTER   build() → app.js                  ← one artifact, promoted as-is
        On each request, Symfony adds to the HTML:
        <script type="application/json" id="rc-app">{"apiBaseUrl":"https://api.example.com"}</script>

The problem

In a Symfony + JavaScript application, variables from .env often need to reach the browser (API URLs, third-party keys, feature flags, etc.). The common solution is to bake them in at build time via DefinePlugin (Webpack) or define (Vite).

Consequences:

  • The build artifact is tied to one environment — promoting from staging to prod requires a full rebuild
  • Rotating a key or changing config requires a full frontend rebuild and redeployment
  • Frontend variables and Symfony .env become two separate systems to maintain

The solution

env-to-js lets Symfony inject .env variables into the HTML at runtime, as typed <script type="application/json"> tags read by a zero-dependency TypeScript reader.

Installation

composer require elfennol/env-to-js-symfony-bundle
npm install @elfennol/env-to-js

Setup

1. Add the Twig block to your base template

{# templates/base.html.twig #}
{% block env_to_js %}
    {{ env_to_js_scripts()|raw }}
{% endblock %}

2. Create a provider

// src/Config/AppConfigProvider.php
namespace App\Config;

use Elfennol\EnvToJs\ConfigProviderInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\Request;

final readonly class AppConfigProvider implements ConfigProviderInterface
{
    public function __construct(
        #[Autowire(env: 'API_BASE_URL')] private string $apiBaseUrl,
        #[Autowire(env: 'CDN_BASE_URL')] private string $cdnBaseUrl,
    ) {}

    public function supports(Request $request): bool { return true; }
    public function getScope(): string { return 'app'; }

    public function getConfig(): array
    {
        return [
            'apiBaseUrl' => $this->apiBaseUrl,
            'cdnBaseUrl' => $this->cdnBaseUrl,
        ];
    }
}

No registration needed — any class implementing ConfigProviderInterface is auto-tagged.

Conditional providers

A provider that only runs on specific pages inspects the request:

// src/Config/MapConfigProvider.php
namespace App\Config;

use Elfennol\EnvToJs\ConfigProviderInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\Request;

final readonly class MapConfigProvider implements ConfigProviderInterface
{
    public function __construct(
        #[Autowire(env: 'MAP_API_KEY')] private string $mapApiKey,
        #[Autowire(env: 'MAP_ID')] private string $mapId,
    ) {}

    public function supports(Request $request): bool
    {
        return in_array($request->attributes->get('_route'), ['app_map_index', 'app_map_show'], true);
    }

    public function getScope(): string { return 'map'; }

    public function getConfig(): array
    {
        return [
            'mapApiKey' => $this->mapApiKey,
            'mapId'     => $this->mapId,
        ];
    }
}

The <script id="rc-map"> tag is injected only on the matched routes.

Configuration

# config/packages/env_to_js.yaml
env_to_js:
    id_prefix: 'rc-'  # default

The injected tag id is {id_prefix}{scope}. Pass the same value as the selector to createConfigReader().

Scope collision

If two active providers return the same scope, their configs are merged with array_merge. Last registered wins on key conflict.

Migration guides

From DefinePlugin / process.env

  1. Remove the configureDefinePlugin block from your Webpack config (or define from your Vite config).
  2. Group your formerly-injected variables into one or several ConfigProviderInterface providers.
  3. Replace each process.env.MY_VAR in your source code with getAppConfig('myVar') (the getter returned by createConfigReader).

Your webpack/vite artifact is now environment-agnostic — one build, promoted from staging to prod without a rebuild.

From window.* or data-options attributes

  1. Create ConfigProviderInterface providers for each group of variables currently assigned on window or in data-* attributes.
  2. Replace window.myVariable accesses with getAppConfig('myVariable') (the getter returned by createConfigReader).
  3. Remove the inline <script> or data-* injection from your templates.

Requirements

  • PHP >= 8.2
  • Symfony >= 7.4

Development

make qa-vendor-install # install dependencies
make qa                # phpcs + phpstan + phpunit + rector
make help              # list all available commands

License

MIT

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk