npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@lasmedia/dynamic-config

v1.0.11

Published

A TypeScript library for managing dynamic configuration in web applications with type safety and runtime validation using Zod schemas.

Downloads

122

Readme

Dynamic Config

A TypeScript library for managing dynamic configuration in web applications with type safety and runtime validation using Zod schemas.

Features

  • 🔒 Type-safe configuration with Zod schema validation
  • 🔄 Dynamic runtime loading from JSON files
  • 🌍 Environment variable integration
  • Vite plugin for seamless build integration
  • 🏗️ Framework agnostic - works with any frontend framework

Installation

npm install @lasmedia/dynamic-config
# or
yarn add @lasmedia/dynamic-config
# or
pnpm add @lasmedia/dynamic-config

Quick Start

1. Define Your Configuration

import { z } from 'zod';
import { defineConfigEntry } from '@lasmedia/dynamic-config';

const appConfig = defineConfigEntry({
  name: 'app',
  schemaJson: z.object({
    apiUrl: z.string(),
    features: z.object({
      darkMode: z.boolean().default(false),
    }),
  }),
  schemaEnv: z.object({
    NODE_ENV: z.enum(['development', 'production']).default('development'),
    DEBUG: z.string().optional(),
  }),
  config: (data) => ({
    apiEndpoint: data.apiUrl,
    isDevelopment: data.NODE_ENV === 'development',
    isDebugMode: data.DEBUG === 'true',
    features: data.features,
  }),
});

2. Setup Vite Plugin

// vite.config.ts
import { defineConfig } from 'vite';
import { dynamicConfig } from '@lasmedia/dynamic-config/vite';

export default defineConfig({
  plugins: [
    dynamicConfig({
      definitionDirs: ['src/config'], // Where to find config definitions
      configJsonDir: './', // Directory for JSON config file
      configJsonFileName: 'env.json', // Config file name
    }),
  ],
});

3. Initialize Configuration at Runtime

import { initConfig, getConfig } from '@lasmedia/dynamic-config';
import { generateConfig } from './dynamicConfig.gen.ts';

// Initialize configuration from remote source
await initConfig(generateConfig, {
  env: import.meta.env,
});

// Access configuration anywhere in your app
const config = getConfig();
console.log(config.app.apiEndpoint);

Core Functions

defineConfigEntry

Creates a type-safe configuration entry that combines JSON and environment variable sources.

const config = defineConfigEntry({
  name: 'myConfig',
  schemaJson: z.object({
    /* JSON schema */
  }),
  schemaEnv: z.object({
    /* ENV schema */
  }),
  config: (data) => ({
    /* transform function */
  }),
});

initConfig

Loads configuration from a remote JSON source and .env file and applies validation and transformation.

await initConfig(generateConfig, {
  configUrl: '/env.json',
  cacheBusting: true,
  fetchOptions: {},
  env: import.meta.env,
});

Options reference

| Option | Type | Default | Description | When to use | | -------------- | --------------- | --------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | | configUrl | string | '/env.json' | URL (relative or absolute) of the JSON config file to fetch at runtime. | Your config file lives elsewhere / you renamed it. | | cacheBusting | boolean | true | Appends a ?t=TIMESTAMP query param to avoid HTTP caching during development. | Disable (set false) if you handle caching headers yourself. | | fetchOptions | RequestInit | {} | Extra options merged into the fetch call (e.g. credentials, headers). | You need auth headers, custom mode, etc. | | env | ImportMetaEnv | import.meta.env snapshot at call time | Override or defer environment snapshot sourcing. Function form evaluated on each call. | HMR in dev mode on .env* change is needed or custom environment injection in tests. |

getConfig & isConfigLoaded

Access the loaded configuration and check if it's been initialized.

const config = getConfig(); // Get current config
const loaded = isConfigLoaded(); // Check if config is loaded

dynamicConfig (Vite Plugin)

Vite plugin that automatically discovers configuration definitions and handles build-time setup.

dynamicConfig({
  definitionDirs: ['src/config'],
  configJsonDir: './',
  configJsonFileName: 'env.json',
});

Plugin options reference

| Option | Type | Default | Description | When to change | | -------------------- | ---------- | ------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | | definitionDirs | string[] | ['src/env'] | Directories scanned recursively for defineConfigEntry calls. | Your config files live elsewhere (e.g. src/config). | | configJsonDir | string | './' | Output folder (relative to project root) where the runtime JSON config file is expected / copied. | You want the file in public/ or a nested folder. | | configJsonFileName | string | 'env.json' | Name of the runtime JSON config file served by the dev server / deployed statically. | You prefer a different name like app-config.json. |

Notes:

  • The plugin generates a dynamicConfig.gen.ts file with a generateConfig export (do not edit manually).
  • The JSON file (env.json by default) must be hosted so the browser can fetch it at runtime (configUrl in initConfig).
  • If you change configJsonDir + configJsonFileName, keep initConfig({ configUrl: '/yourName.json' }) consistent.

Environment variables & HMR behavior (dev mode only)

Vite injects import.meta.env at module transform time. This means:

  • Every module receives a snapshot of environment values at the moment it is (re)compiled.
  • The object is NOT reactive; values do not change inside an already executed module.

By default initConfig reads import.meta.env when you call it. If you call it exactly once during application startup, the same snapshot will be kept until you fully reload the page. Restarting the dev server without a browser reload will usually NOT re-run the already cached module in the tab. (except using --force flag)

Optional override / provider

You can pass a custom environment source via the env option:

await initConfig(generateConfig, {
  env: import.meta.env, // explicit snapshot now
});

Do you need this?

Usually no. If you are fine with either:

  • doing a full browser reload after changing .env* files, or
  • running dev with yarn dev --force,

then the default behavior (no env option) is enough.

Use the env option only if you explicitly want to control when the snapshot is taken.

Configuration Sources

The library supports two main configuration sources:

  • JSON files - External configuration loaded at runtime
  • Environment variables - Build-time and runtime environment variables

Both sources are validated using Zod schemas and merged into a single configuration object.

Example Project Structure

src/
├── config/
│   └── appConfig.ts      # App configuration definition
├── main.ts
└── ...
env.json                  # Runtime configuration file (root)

License

MIT