tnl.dev :: docs
next.js.
connect Next.js to tnl dev from the consolidated tnl package.
The Next.js integration is part of @tnldotdev/tnl. It preserves the project's Next.js
configuration and reports the actual post-bind development target to tnl dev. During ordinary
development it exposes generated project metadata without changing networking; builds remain inert.
install and initialize
Next.js 16.3.4 or newer and Node.js 22.18 or newer are required. Install one package:
npm install --save-dev @tnldotdev/tnl@next
npm exec -- tnl initwrap the next.js config
Pass the existing configuration to withTnl:
import { withTnl } from "@tnldotdev/tnl/next";
export default withTnl({
reactStrictMode: true,
});The wrapper also accepts promised configuration and synchronous or asynchronous configuration
functions. Existing options are preserved. withTnl accepts only the Next.js configuration value,
not route options.
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:
export default {
team: "Acme Engineering",
services: {
web: {
directory: ".",
dev: { command: ["next", "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:
npm exec -- tnl devIf 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 wrapper only injects
previously generated project metadata.
how port discovery works
Next.js keeps its default or explicitly configured host and port behavior. The integration reads the
actual listener target after Next.js binds and registers that target with tnl. This post-bind step is
important when a preferred port is occupied: tnl publishes the port Next.js actually chose instead
of an unrelated process still listening on the preferred port. The assigned public hostname is
added to allowedDevOrigins without replacing project values.
No explicit Next.js hostname is required. A project can still set --hostname/-H to expose the
development server on the LAN or choose another binding; that listener choice is preserved and is
independent from whether tnl publishes the service.
To require one port for an ad hoc run, pass it to tnl:
tnl dev --port 3000 -- npm run devtnl dev supplies PORT to the child and treats it as exact. If Next.js reports a different
post-bind port, startup fails with TNL_TARGET_MISMATCH instead of publishing the wrong local
process.
project runtime
Server and client application code read public project metadata from the optional root export:
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 wrapper can read
existing generated metadata and sets it to false without changing Next.js networking. 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/next, runtnl devrather than the framework command alone, and reruntnl initto report any integration action still needed. - Next.js selected another port: omit
tnl dev --portto allow normal discovery, or make the configured and forced ports agree. tnl always uses the actual post-bind target. - the public origin is rejected: keep
withTnlaround the exported config; it adds the current assigned hostname without replacing intentionalallowedDevOriginsvalues. - the route stays in provisioning: use
tnl statusfor the service state and follow the diagnostic's contextual help URL rather than changing Next.js networking settings. - visitors are denied: the current-IP policy is working. Add a narrow allow-list entry or use
tnl dev --publicintentionally.
Use development workflow for route reconciliation, ephemeral routes, and aggregate denied visitor warnings.