tnl.dev :: docs

vite.

connect Vite to tnl dev from the consolidated tnl package.

The Vite integration is part of @tnldotdev/tnl. It reports Vite's actual post-bind target to tnl dev while preserving Vite's host and port behavior. During ordinary local development it exposes generated project metadata without changing network settings; previews and builds remain inert.

install and initialize

Vite 6.0.9 or newer and Node.js 22.18 or newer are required. Install one package:

install tnl
npm install --save-dev @tnldotdev/tnl@next
npm exec -- tnl init

add the plugin

Add tnl() to the project's Vite plugins:

vite.config.ts
import tnl from "@tnldotdev/tnl/vite";
import { defineConfig } from "vite";
 
export default defineConfig({
  plugins: [tnl()],
});

tnl() accepts no route options. Keep Vite settings in vite.config.ts; keep the selected team, named services, hostname, visitor policy, and development commands in project configuration.

configure a named service

tnl init can create the service entry. The TypeScript form is:

tnl.config.ts
export default {
  team: "Acme Engineering",
  services: {
    web: {
      directory: ".",
      publish: { target: 5173 },
      dev: { command: ["npm", "run", "dev"] },
    },
  },
};

No explicit hostname is needed for normal development. tnl derives it from the Git worktree and the web service name, after applying command, environment, and project-config precedence.

Start the public workflow with the project-local client:

run Vite through tnl
npm exec -- tnl dev

If no saved hosted session is usable, tnl opens browser authentication automatically and resumes the same command after sign-in. Ordinary npm run dev remains local; the plugin only injects previously generated project metadata.

how port discovery works

Vite can keep its normal next-port behavior when the preferred port is occupied. The plugin reports the final post-bind target rather than assuming server.port succeeded, so tnl cannot accidentally publish another process on the preferred port. During the public workflow, the plugin adds the assigned hostname to server.allowedHosts while preserving existing server settings. Vite's default host remains the default; an explicit server.host can independently expose the service on the LAN without changing tnl route policy.

To require one port for an ad hoc run, pass it to tnl:

require port 4173
tnl dev --port 4173 -- npm run dev

The plugin lets Vite report its final listener for that run, then tnl requires the registered port to match exactly. A fallback port fails with TNL_TARGET_MISMATCH instead of publishing the wrong local process. Middleware mode is unsupported because there is no listening port to register.

project runtime

Application code reads public project metadata from the optional root export:

src/project.ts
import { tnl } from "@tnldotdev/tnl";
 
tnl?.memberNamespace;
tnl?.services.web.hostname;
tnl?.services.web.url;
tnl?.runningUnderTnlDev;

Before tnl dev starts the child, tnl writes .tnl/project.json and .tnl/project.d.ts. Include .tnl/project.d.ts in the application's TypeScript inputs. It augments tnl.services with exact service keys and literal member-namespace, hostname, and HTTPS URL values from the selected team and routes.

During tnl dev, runningUnderTnlDev is true. During ordinary npm run dev, the plugin can read existing generated metadata and sets it to false without changing Vite's listener. The export is undefined when metadata is absent and during builds and previews. tnl publish cannot inject metadata into an already-running application process. No saved authentication or server access token is exposed.

troubleshooting

  • the integration did not connect: confirm the import is exactly @tnldotdev/tnl/vite, run tnl dev rather than the framework command alone, and rerun tnl init to report any integration action still needed.
  • Vite selected another port: omit --port to allow discovery, or make the configured and forced ports agree. tnl always uses the actual post-bind target.
  • the public hostname is rejected: preserve the plugin in vite.config.ts; it adds only the assigned hostname and does not remove existing allowedHosts entries.
  • the route stays in provisioning: use tnl status for the service state and follow the diagnostic's contextual help URL rather than changing Vite host settings.
  • visitors are denied: the current-IP policy is working. Add a narrow allow-list entry or use tnl dev --public intentionally.

Use development workflow for route reconciliation, ephemeral routes, and aggregate denied visitor warnings.