All guidesMore providers: databases, auth, hosting, email and payments
On this page

SvelteKit error

SvelteKit

Cannot import $env/static/private into code that runs in the browser, as this could leak sensitive information

By Ohad Krispin, founder of VibeRaven · LinkedIn

"Cannot import $env/static/private into code that runs in the browser, as this could leak sensitive information" is a build error from SvelteKit. Some file that also ships to the browser imports the private environment module, directly or through a chain of imports that the message prints. The build stops before a secret can reach a JavaScript bundle. Move the read into a server-only file (src/lib/server/..., a file named *.server.js, +page.server.js or hooks.server.js) and give the browser only the result. A value the browser may see goes in $env/static/public with the PUBLIC_ prefix. SvelteKit 3 prints the same message for $app/env/private. Reproduced with @sveltejs/kit 2.70.3 and 3.1.1 on 2026-10-31.

What the import chain in the message means

The build walks back from the private module to a route, a universal load function or a hooks file, and prints each file as an indented list of imports. The first file in the list is the one that runs in the browser; the last line is the private module. In the run on 2026-10-31 a +page.svelte imported $lib/secrets.js, which imported the module. The block below shows the message with the backticks around import type left out. SvelteKit treats the whole chain as unsafe even when the page uses only an export that has nothing secret in it, because the secret code could end up in the JavaScript the browser downloads.

Cannot import $env/static/private into code that runs in the browser, as this could leak sensitive information.

 src/routes/+page.svelte imports
  $lib/secrets.js imports
   $env/static/private

If you're only using the import as a type, change it to import type.

How do I use a private variable from a page or a component?

Read it in a file the browser never gets, and pass the page only what it needs. The SvelteKit docs say the private module can be imported only into modules that run on the server, such as hooks.server.js or +page.server.js, and that any file becomes server-only with a server segment in its name or by living in a server directory such as src/lib/server. After this change the same build got past this check in the 2026-10-31 run.

// src/lib/server/billing.js
import { STRIPE_SECRET_KEY } from '$env/static/private';
export const billingConfigured = Boolean(STRIPE_SECRET_KEY);

// src/routes/+page.server.js
import { billingConfigured } from '$lib/server/billing.js';
export const load = () => ({ billingConfigured });

Which module do I use: $env/static/private, $env/dynamic/private or $env/static/public?

The static modules are fixed into the bundle at build time and the dynamic ones are read when the app runs. The private modules cannot be imported into client code. $env/static/public can be imported into client code and only holds variables that start with the public prefix, PUBLIC_ by default, so its values are visible to the browser. The adapter-vercel docs recommend $env/static/private for the variables Vercel provides, because they are the same at build time and at run time. In SvelteKit 3 the $env modules are deprecated in favour of $app/env/private and $app/env/public, and the docs say they will be removed in SvelteKit 4.

Checklist

How to fix it, then check the fix

  1. 01
    Find every file that imports a private env module:
    grep -rnE "\$env/(static|dynamic)/private|\$app/env/private" src
  2. 02Start from the first file in the import chain the message printed. That is the file the browser loads. Follow the list down to the file that holds the import.
  3. 03Move the code that needs the secret into src/lib/server/, a file named *.server.js or *.server.ts, or a +page.server.js, +layout.server.js, +server.js or hooks.server.js file.
  4. 04Pass the page or component a plain value from a load function or a form action, not the secret and not the module.
  5. 05If the browser needs the value, rename the variable with the PUBLIC_ prefix and import it from $env/static/public. Do this only for values that are safe to show to every visitor.
  6. 06Run npm run build. The error is raised by the build, so a clean build means no browser file reaches the private module.

Related

Sources

Official docs behind this checklist

FAQ

FAQ for builders before launch

Why does a type-only import also fail?

The message ends with a hint: if you only use the import as a type, change it to import type. A type import is removed when the code is compiled, so it does not pull the private module into the browser bundle.

Does Vitest hit this error?

No. The SvelteKit docs say Vitest and similar test runners do not make the server and browser distinction, so the detection is turned off while tests run. That also means a passing test run does not show that the build will pass.

Can I import a PUBLIC_ variable from $env/static/private?

No. The SvelteKit docs say a variable that starts with the public prefix is left out of the private module, and importing it from there throws an error during the build. Import it from $env/static/public instead.