Create Desktop Apps with pywebview#

Convert Panel applications into standalone desktop executables using pywebview and package them for distribution.

Install Dependencies#

pip install panel pywebview pyinstaller

Note

Linux requires additional GTK dependencies. See pywebview installation guide.

Create Your Panel Application#

We’ll build a simple Panel application with optional exit functionality:

  • a slider to choose a number

  • a corresponding row of ⭐ characters

  • an Exit button that terminates the desktop application

The full implementation of this Panel app (via a create_app() function) is shown in the complete app.py example in the next section.

Wrap with pywebview#

Create app.py to wrap your Panel application:

import webview
import panel as pn
import threading
import time
import os
import socket

class PanelDesktop:
    """Serve Panel apps as desktop applications using webview."""

    def __init__(self, title="Panel Desktop App", width: int=600, height: int=600):
        self.title = title
        self.width = width
        self.height = height

    @staticmethod
    def _find_free_port():
        """Find a free port on localhost."""
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.bind(('', 0))
            s.listen(1)
            port = s.getsockname()[1]
        return port

    def serve(self, func_or_path, port: int=0):
        """Serve a Panel function or file in a desktop window."""
        if not port:
            port = self._find_free_port()
        # Start Panel server in daemon thread
        server_thread = threading.Thread(
            target=lambda: pn.serve(func_or_path, port=port, show=False, autoreload=False),
            daemon=True
        )
        server_thread.start()

        # Create and start webview
        webview.create_window(
            self.title,
            f'http://localhost:{port}',
            resizable=True,
            fullscreen=False,
            width=self.width,
            height=self.height,
            text_select=True)
        webview.start()

def create_app():
    """Example Panel app with exit functionality."""
    pn.extension(design="material")

    pn.pane.Markdown.disable_anchors = True

    slider = pn.widgets.IntSlider(value=3, start=1, end=5)
    stars = pn.bind(lambda n: "⭐" * n, slider)

    def exit_app(event):
        """Exit the desktop application."""
        os._exit(0)
        # an alternative way:
        # import webview
        # webview.windows[0].destroy()

    exit_btn = pn.widgets.Button(name="Exit", on_click=exit_app, button_type="primary")

    return pn.Column(
        "# Desktop Panel Application",
        "This is a Panel app running in a native window!",
        slider, stars, exit_btn
    )

if __name__ == "__main__":
    desktop = PanelDesktop("My Panel App")
    desktop.serve(create_app)

Note

You can serve an external file instead of a function:

desktop.serve("my_panel_app.py")

Test the Application#

Run:

python app.py

The app should look like:

Panel Desktop PyWebView

Verify all functionality works before packaging.

Package for Distribution#

Packaging frameworks by desktop platform:

See the pywebview freezing guide for complete platform-specific instructions.

Package for Windows#

Build with PyInstaller:

pyinstaller app.py

The executable will be in the dist folder.

Options:

  • --debug all - Enable debugging

  • --icon=favicon.ico - Add custom icon (download Panel icon)

  • --noconsole - Hide console window

  • --onefile - Create single executable (makes the loading of the application slow)

Package for MacOS#

On OSX you will need py2app instead of pyinstaller. Begin by generating a setup.py for your application with

py2applet --make-setup app.py

Next build it with:

python setup.py py2app

py2app bundles your local environment, since conda uses features not supported in a packaged application you cannot use a conda environment as your build environment.

Troubleshooting#

Blank window or errors:

  • Add hidden imports: --hidden-import=panel --hidden-import=bokeh

  • Run without --noconsole to see error messages

Create installer for Windows#

For professional distribution, create an installer using InstallForge, NSIS, or Inno Setup.

Alternative: Distribute as UV Tool#

A simple alternative to PyInstaller, would be to distribute the app as a uv tool.