| title | NativeScript |
|---|---|
| description | Add the devtools to a NativeScript Angular app and send live components, signals, injectors, stores, change detection, leaks and errors from a simulator or device to your machine. |
A NativeScript Angular app renders into @nativescript/core views, so it has no DOM for the browser overlay to walk. The @pangular-inspector/devtools/overlay-nativescript entry point walks the NativeScript view tree through *Angular's debug API instead, and sends live data to the standalone devtools server on your machine.
This page sets the overlay up, then describes what it shows and how it works.
- A development build. Angular publishes its debug API on
globalThis.ngonly whenngDevModeis on. Start the overlay inside anif (__DEV__)check. @nativescript/core, an optional peer dependency of the package. The overlay reads the root view and the platform from it.- A
WebSocketglobal. The NativeScript runtime has none, so import@valor/nativescript-websocketsfirst insrc/polyfills.ts. Without it, the overlay logs a warning that names the package and does not start. initNativeScriptOverlay()called beforerunNativeScriptAngularApp(), so the injector inspector gets its provider lists. See How it works.- A server started with
--no-auth, since the app cannot enter the one-time code the panel asks for. Against a server with auth on, the overlay logs a warning that says to start it with--no-auth, and tries again everyretryMs.
@pangular-inspector/devtools and @valor/nativescript-websockets.
Import @valor/nativescript-websockets first in src/polyfills.ts.
Call initNativeScriptOverlay() in src/main.ts, before the app bootstraps.
Start pangular dev --no-auth in the app's folder and open the panel.
pnpm add @pangular-inspector/devtools @valor/nativescript-websocketsnpm install @pangular-inspector/devtools @valor/nativescript-websocketsyarn add @pangular-inspector/devtools @valor/nativescript-websocketsbun add @pangular-inspector/devtools @valor/nativescript-websocketsThe overlay talks to the server over a WebSocket, and the NativeScript runtime has no WebSocket global. Import the polyfill first:
// src/polyfills.ts
import '@valor/nativescript-websockets';// src/main.ts
import {
bootstrapApplication,
provideNativeScriptRouter,
runNativeScriptAngularApp,
} from '@nativescript/angular';
import {initNativeScriptOverlay} from '@pangular-inspector/devtools/overlay-nativescript';
import {routes} from './app/app.routes';
import {AppComponent} from './app/app.component';
if (__DEV__) {
initNativeScriptOverlay();
}
runNativeScriptAngularApp({
appModuleBootstrap: () =>
bootstrapApplication(AppComponent, {
providers: [provideNativeScriptRouter(routes)],
}),
});Call it before runNativeScriptAngularApp(), as above. The injector inspector relies on *Angular's injector profiler, which *Angular only wires while it creates the platform. See Options for baseURL, intervalMs and retryMs.
| Target | Default URL |
|---|---|
| iOS simulator | http://localhost:9999/ |
| Android emulator | http://10.0.2.2:9999/ |
| Physical device | Pass { baseURL }, or inject the LAN address at build |
A build that defines __PANGULAR_LAN_URL__ takes that URL over the defaults above. baseURL still wins over both.
For a physical device, pass the address of your machine as baseURL, or let the build inject it, so main.ts keeps no address. Start the server with --lan-address (see Run the devtools server) to print the URL, then define it in webpack.config.js from an environment variable:
// webpack.config.js
const webpack = require('@nativescript/webpack');
module.exports = (env) => {
webpack.init(env);
const lanUrl = process.env['PANGULAR_LAN_URL'];
if (lanUrl && !env.production) {
webpack.chainWebpack((config) => {
config.plugin('DefinePlugin').tap((args) => {
Object.assign(args[0], {__PANGULAR_LAN_URL__: JSON.stringify(lanUrl)});
return args;
});
});
}
return webpack.resolveConfig();
};Run the app with the URL the server printed, for example PANGULAR_LAN_URL=http://192.168.1.20:9999/ ns run android. initNativeScriptOverlay() without baseURL then connects there. A release build (--env.production) gets no address.
The devtools server speaks plain http. Android 9 and later block cleartext traffic, and iOS App Transport Security can block it, so allow it to your machine in development builds.
On Android, list the hosts the app may reach over cleartext in App_Resources/Android/src/main/res/xml/network_security_config.xml. The emulator reaches your machine at 10.0.2.2, and a device at your machine's address:
<?xml version="1.0" encoding="utf-8"?>
<!-- App_Resources/Android/src/main/res/xml/network_security_config.xml -->
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="false">localhost</domain>
<domain includeSubdomains="false">10.0.2.2</domain>
<domain includeSubdomains="false">192.168.1.20</domain>
</domain-config>
</network-security-config>Then set android:networkSecurityConfig="@xml/network_security_config" on the <application> element of App_Resources/Android/src/main/AndroidManifest.xml. If the manifest already sets android:usesCleartextTraffic="true", every host is allowed already. examples/nativescript ships a network security config. Keep these entries out of release builds.
On iOS, add the local networking exception to App_Resources/iOS/Info.plist:
<!-- App_Resources/iOS/Info.plist (excerpt) -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true />
</dict>
<key>NSLocalNetworkUsageDescription</key>
<string>Connects to Pangular Inspector on your computer during development.</string>On iOS 14 and later, the first connection to an address on your network shows a local network permission prompt with that text. If the app was denied, turn it back on in Settings > Privacy & Security > Local Network.
Run the server in the app's folder, so the source scanners read its src/:
cd my-nativescript-app
npx @pangular-inspector/devtools dev --no-authThe server listens on localhost only, which the iOS simulator and the Android emulator reach. --no-auth is needed because the app cannot enter the one-time code the panel asks for.
| What | Where |
|---|---|
| Devtools UI | http://localhost:9999/ |
| MCP endpoint | http://localhost:9999/__mcp |
A physical device reaches your machine over the network, so the server has to listen on an interface the device can reach:
npx @pangular-inspector/devtools dev --host 192.168.1.20 --allowed-origin http://192.168.1.20:9999 --no-auth --lan-addressThe server then listens on that address only, not on localhost, so open the panel at http://192.168.1.20:9999/. --allowed-origin lets the panel's own origin through there. --lan-address prints the URL to inject into the app. Use your machine's address and the server's port.
--host and --no-auth, every host that can reach that address can call the devtools RPC and MCP endpoints without a code. Use it on a trusted network only. Binding to the one address the device uses, as above, keeps the server off your other interfaces, which 0.0.0.0 would not.
See Standalone CLI for the other server options and Security for what --no-auth turns off.
| Tab | On NativeScript |
|---|---|
| Components | The component tree, paths and the detail panel. Pointing at a component in the panel draws a box over it on the device, and Pick component on device selects the component you tap. Change detection records cycles with their causes and OnPush hints, and Leaks lists effects and subscriptions, with navigations counted from the app's Router. |
| Signals | The signal graph of the selected component, with its write history. |
| Injectors | Element injectors and environment injectors with their providers. Show value reads a created provider's value, and Startup shows the Angular version and when the app was first stable. |
| Store | Live @ngrx/signals and @ngrx/store state, the change log, sessions and restore, and NGXS stores. The store list names the app NativeScript (iOS) or NativeScript (Android). |
| Queries | The TanStack Query clients the app injected, with their queries and mutations. |
| Pipes | The pipes in use, with their instances and the components that use them. |
| Errors | Errors from Angular's ErrorHandler and the Application uncaughtError and discardedError events, the console, and the NG0100 and missing provider explainers. |
| Audit | The findings of change detection, leaks and errors, and before and after sessions. axe has no DOM to check. |
| Plugins | The views of page plugins the app registers. |
The source scans (routes, pipes, NgRx declarations) come from the server, so they work as with the Standalone CLI. There is no in-app popup.
The server's configuration applies to the app: inspectors you turn off there are not collected, and redaction.secretNames masks values before they leave the device.
These features read or draw the DOM, or run in the browser, so a NativeScript app has none of them. The panel hides the live views for the app, Run axe says the app has no DOM, and the agent tools answer the same instead of asking another tab.
| Feature | Why it is missing |
|---|---|
| Flash checks, Performance track and Angular profiling | They draw over DOM elements or into the browser's Performance panel. |
| Checks that changed nothing | The recording counts them by watching DOM updates. |
| Destroyed and retained instances in Leaks | A development build keeps every destroyed instance in Angular's DI debug data, so only a detached view would show a leak, and telling one apart asks Angular about a DOM element. |
| Provider construction times in Startup | They need the startup hook, which loads before *Angular in a browser. |
axe, screenshot-component, hydration and the console handles |
They read or copy the DOM. |
| Router, Forms and SSR & HTTP tabs | The NativeScript overlay does not collect them. |
Hovering a row still draws a box over the component on the device.
The live data shows in the same tabs as a browser page.
| Server | Where the app shows |
|---|---|
| Standalone CLI | The panel at http://localhost:9999/, without ?view. |
| A hub (Express or the Vite plugin) | The Angular dock in the side rail. |
The NativeScript dock (?view=nativescript) shows no live data. It is a setup card, Inspect NativeScript apps, with the setup steps and a link to this page.
The overlay marks its component reports with platform: 'nativescript', the URL / and the title NativeScript (iOS) or NativeScript (Android). list-pages lists the app with the NativeScript platform. See Agent tools.
initNativeScriptOverlay() takes an optional object:
| Option | Default | What it does |
|---|---|---|
baseURL |
lanDevtoolsURL(), then defaultDevtoolsBaseURL() |
Absolute URL of the devtools server. |
intervalMs |
The server's limits.refreshMs |
How often the app reports, in milliseconds. |
retryMs |
5000 |
How long to wait before connecting again after a failure or a dropped socket. |
defaultDevtoolsBaseURL(port = 9999) is exported from the same entry point and picks the address by platform:
| Platform | Default baseURL |
|---|---|
| iOS | http://localhost:9999/ |
| Android | http://10.0.2.2:9999/ |
10.0.2.2 is how the Android emulator reaches its host. The devtools server answers requests sent to it (and to 10.0.3.2, Genymotion's address for the host) from the emulator without any option, as it does localhost. A physical device needs your machine's LAN address, as baseURL or injected by the build.
lanDevtoolsURL(), exported from the same entry point, returns the URL a development build defined as __PANGULAR_LAN_URL__, or undefined. When it returns one, the overlay connects there instead of to defaultDevtoolsBaseURL(). See Use the injected LAN address and Allow plain HTTP to your machine.
initNativeScriptOverlay() returns a function that stops the overlay and tells the server to forget every report the app sent.
- Host tree: the overlay walks the
@nativescript/coreviews under the app's root view with the same collectors as the browser overlay. It starts at the host of the root component, found through *Angular's debug API, and looks the root up again on every walk, since NativeScript replaces the root view on some navigations. A host is named by its component's selector. - Injector profiler: *Angular wires the profiler that backs the provider lists only when a
windowglobal exists as the platform is created. The overlay defineswindowuntil *Angular publishesng.getComponent, then removes it. Ifwindoworngalready exist, it leaves them alone. - Web shims: the devframe client reads
locationandnavigator. The overlay defines them frombaseURLwhen the runtime has none, and leaves them in place. - Transport: the overlay connects over a WebSocket only, since the NativeScript
fetchhas no streaming body. - Store stand-in:
initNativeScriptOverlay()puts the Redux DevTools stand-in in place before the app starts, so NGXS stores report without thestore-hookentry. A store library that looks for the extension onwindowfinds nothing there, since a NativeScript app has nowindowonce it started. When the server turns thengrxinspector off, the overlay removes it again. - Angular version: change detection recording and Startup read the version from the
ng-versionvalue *Angular sets on the root view. When the renderer keeps none, recording stays off and the Change detection section says the page has no profiler, as in a browser, since taking Angular's profiler without knowing the version could evict another one for good. - Reporting: the app collects every
intervalMsand sends a report when it changed. The Leaks scan walks the views again when the component tree changed, and at least every 30 seconds. When a report has not changed for a while, the app asks the server whether it still holds it, and sends it again only if not. When the connection fails or drops, it logs one warning and tries again everyretryMs. The app keeps one page id for as long as the overlay runs, so a reconnect does not show it as a new page. - Highlight: when the panel or the
highlightagent tool points at a component, the overlay adds a native view of its own to the app's window, aUIViewon iOS and aViewon Android, with a 2 px#68b6ffborder over the first view under the host that draws something. The box ignores touches, and no view of the app changes. It goes away when the pointer leaves the row, after the duration thehighlighttool asks for, or after two seconds. If the overlay can't add the view, it sets the border on the app's view instead and restores it afterwards. - Pick: Pick component on device adds a clear view over the whole window that catches the next tap, so the tap doesn't reach the app. The overlay finds the deepest laid-out view under the tap, selects the component that hosts it, and removes the view. The arrow keys in the panel move between components as in a browser, with the box on the one shown. A pick ends after 15 seconds without a tap.
examples/nativescript is a ns create --ng project wired up this way, with a small showcase component (signals, a computed, an effect and a component-level provider). It is tested on the iOS simulator. Android is untested.
The demo maps @pangular-inspector/devtools/* to the package's build output in packages/devtools/dist, so build the package first:
pnpm devtools:build-pkg
pnpm devtools:nativescriptpnpm devtools:nativescript starts the devtools server on localhost, scanning examples/nativescript/src, and keeps running. In a second terminal, install and start the app:
cd examples/nativescript
npm install
ns debug ios --no-hmrFor a physical device, pnpm devtools:nativescript:device listens on every interface instead, with the same warning as above.
The troubleshooting page lists the problems every setup shares, by symptom.
Check the app log for a[pangular] line. A warning about a missing WebSocket global means @valor/nativescript-websockets is not imported first in src/polyfills.ts. A warning about reaching the server means the address is wrong for where the app runs; see where the overlay connects. A release build has no ng global, so nothing is collected there.
The overlay has to start before runNativeScriptAngularApp() creates the platform. Call initNativeScriptOverlay() above it in src/main.ts.
The dock is a setup card. The app's data shows in the Angular dock, or in the panel without ?view on the standalone CLI.