| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [View Raw Code] [Original HTTPS Page] |
pn preview renders your app in a browser tab, inside a phone frame, with Fast Refresh on every save. It's the fastest way to build UI in PythonNative: edit a component, save, and see the result in well under a second, with no simulator boot, no device deploy, and no native build.
pn previewThe browser renderer speaks the same bridge protocol as the Swift and Kotlin renderers. Your components, hooks, reconciliation, logical navigation, and async work run in the pn preview Python process. The page implements widgets with DOM elements, layout with Yoga WebAssembly, and animation graphs with JavaScript. Platform controls, fonts, and device APIs differ from those in a mobile app.
From a project directory (one created by pn init, with an app/main.py that defines App):
pn previewThis starts the dev server, opens http://localhost:8765/?token=... in your default browser (the page trades your dev token for a cookie and drops it from the address bar), mounts your App in a phone frame, and watches app/ for changes. Edit any component and save: the page updates in place while preserving component state (counters, text input, scroll position, the navigation stack).
pn preview imports your real app code, so install your project's dependencies in the same environment first (pip install whatever you declared in [requirements].packages). pn preview warns about missing ones on startup. If an import fails, the preview logs the traceback in its console and the terminal; install the package or fix the code and save to recover.
pn preview # the project entry point (app/main.py -> App)
pn preview app.screens.home # mount a different module's App
pn preview --port 9000 # another port
pn preview --host 127.0.0.1 # don't expose the server on the LAN
pn preview --no-open # start the server, open the tab yourself
pn start # the same server without opening a browserpn start and pn preview are the same command; pn preview only adds opening the tab. Simulators and devices launched with pn run connect to the same server, so one terminal drives every client.
Along the top of the page:
The status dot shows the WebSocket connection. If you stop pn preview the page waits and reconnects when the server returns; the app is mounted fresh on reconnect.
pn preview sets PN_PLATFORM=web and runs [pythonnative.preview.serve][pythonnative.preview.serve], which:
Layout is owned by the engine, not the DOM: the flex layout engine computes an absolute frame for every element, and the page positions each element with that frame. Text is measured by the page with the same font it renders, so wrapping and intrinsic sizes match what you see.
Root navigators (StackNavigator, TabNavigator, and DrawerNavigator) drive a real stack of screens in the page, the same way they drive UINavigationController and fragments on device. Each pushed screen remains a logical child of the application's shared tree; navigate(...) selects or adds a route, go_back() pops, and covered screens keep their state. Pushes and pops animate with the screen's animation and presentation options.
Each stack draws a navigation bar from the top screen's options, the way the native stacks do: title, header_shown, header_large_title, header_back_visible, header_back_title, header_tint_color, header_style, and header_title_style, with header_left and header_right rendered in the bar. The back button pops through the same path as an iOS swipe back, so a before_remove listener can veto it. A stack at the root of the app replaces the host's own bar, as it does on iOS.
The preview always runs with dev diagnostics on: unknown style keys and duplicate list keys print [PN] WARN messages, hook-order violations raise immediately, and uncaught errors from renders, effects, and event handlers show a full-screen RedBox with the traceback. Fix the code and save; a successful reload clears the overlay. Errors that happen before any screen exists (an import error in app/main.py) print in the terminal and the page console.
Saving a .py file under app/ reloads the changed modules and swaps every affected component function into the live tree in place, so the next render reuses the existing hook state. Edits to a component body keep your counters, form values, scroll positions, and navigation stack. When a clean swap isn't possible, the screen remounts instead so you're never stuck with a stale tree. See the Fast Refresh guide for the mechanics; the preview and device builds share the same engine.
When the preview is running, [Platform.OS][pythonnative.Platform] is "web":
import pythonnative as pn
pad = pn.Platform.select({"web": 12, "ios": 16, "android": 16, "default": 12})Platform.select's "native" key matches iOS and Android only; the browser is a development surface, so use an explicit "web" key (or "default") for it. You can also check [Platform.is_web][pythonnative.Platform] or the pythonnative.utils.IS_WEB flag directly.
The preview is a development tool, optimized for fidelity of layout and logic rather than pixel-perfect platform chrome.
Faithful:
Approximated or absent:
When the chrome matters, verify on device with pn run.
The server binds to all interfaces by default so your own phones can reach it, but it only answers clients that present your dev token. The LAN URLs pn start prints carry the token, and anyone who has one can read your app's source for as long as the token stays the same, so open them only on your own devices. One page drives the app at a time: the newest tab to connect takes over, and the previous one shows a notice until you reload it. Use --host 127.0.0.1 to keep the preview local.
| Back | FazBrowse Home | New Git URL |