Library / SDK
r0x0r/pywebview avatar
r0x0r/pywebview

pywebview: one window call that lands on WinForms, Cocoa, Qt, or GTK

Build GUI for your Python program with JavaScript, HTML, and CSS

6,169 stars643 forksPythonBSD-3-Clause

At a glance

What is it?
A BSD licensed wrapper that puts HTML content in a native desktop window using the platform's own widget library, with a built-in HTTP server and two way calls between JavaScript and Python. Its real constraint is that the native libraries underneath it are your problem to install.
Who is it for?
pywebview fits a Python program that already owns its logic and wants the interface written in HTML, CSS, and JavaScript without shipping a browser runtime in the frozen build. It does not fit a team that wants one rendering path on every machine, since macOS gets WebKit through Cocoa, Windows gets WinForms through pythonnet, and Linux gets Qt or GTK, and those are different environments to test.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • r0x0r/pywebview6,169 ★ · +138
Star history
Repository2026-09-232026-11-14
r0x0r/pywebview6k6.2k

138 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The platform is chosen by the window call, not by your code

A single call creates a window, and the widget library behind it depends on where the program runs. Windows uses WinForms, reached through pythonnet, which the dependency list pulls in only when `sys_platform=='win32'`. macOS uses Cocoa, and that branch of the dependencies is heavier: `pyobjc-core` plus framework packages for Cocoa, Quartz, WebKit, security, and UniformTypeIdentifiers, each at version 9.0 or newer. Linux takes QT or GTK, with `QtPy` declared for OpenBSD. Android is listed among the supported systems as well, and `examples/android_test_app/` exists for it.

The whole hello world is three lines, which is the clearest statement of the design:

python
import webview
webview.create_window('Hello world', 'https://pywebview.flowrl.com/hello')
webview.start()

You pass a title and a URL. Everything native that happens afterwards is chosen for you.

One pip command, and the native libraries are still on you

The documented install is a single line:

bash
pip install pywebview

Under it sits a warning that is easy to skim past: you might need additional libraries, and the installation page is where the details are. That is not hedging. The package metadata carries only the bridges, not the GUI toolkits, and the bridges themselves are conditional. `proxy_tools`, `bottle`, and `typing_extensions` are the only unconditional dependencies in the list, while everything platform specific sits behind an environment marker.

So a fresh install on a bare machine gives you the wrapper and a set of imports that resolve on some systems and fail on others. The cost of the missing pieces differs per platform, which is why the page is worth reading before writing the first window rather than after.

The built-in server is why examples include a Flask app

The package ships with an HTTP server inside it, and `bottle` sits in the dependency list to serve it. That is a design choice with consequences: a pywebview application can serve its own pages and API from inside the process rather than pointing the window at a remote host, which is what the hello world does when it loads a URL.

The example tree shows both shapes. `examples/flask_app/` puts a Flask application in the picture, so you can see the window talking to a Python web stack on the same machine. The standalone examples, such as `examples/change_url.py`, move the window's address without any server framework in between. `examples/cookies.py` and `examples/downloads.py` sit alongside them, which tells you the session and file behaviours that come with owning the window's browser context.

The server being built in also means you are choosing a port and a lifecycle for it, not inheriting one from a framework you did not install.

Getting into Python from the page has three separate entry points

Two way communication between JavaScript and Python is a headline feature, and the examples split it into distinct shapes rather than one. `examples/expose.py` covers pushing Python objects into the page so JavaScript can call them. `examples/evaluate_js.py` runs JavaScript from Python, and `examples/evaluate_js_async.py` is the asynchronous counterpart for work that should not block the window.

DOM support in Python gets its own cluster: `examples/dom_manipulation.py`, `examples/dom_traversal.py`, and `examples/dom_events.py` cover reading and changing the document from the Python side and receiving its events. That is the part that distinguishes this from a browser in a frame: the tree is addressable as objects rather than only through a script string.

The events and window examples form a fourth group: `examples/events.py` for the event system, and `examples/alert.py`, `examples/confirmation_dialog.py`, `examples/confirm_close.py`, and `examples/custom_title_bar.py` for native GUI elements. `examples/frameless.py` and `examples/fullscreen.py` cover window manipulation, `examples/focus.py` and `examples/drag_drop.py` and `examples/drag_region.py` cover input.

A frozen build stays small because no renderer travels with it

The stated benefit of the design is that freezing your application does not add a heavy GUI toolkit or a web renderer to the executable, which keeps the packaged size down. On macOS the renderer arrives through the system WebKit framework the pyobjc dependencies reach, and on Windows the widget library is the platform's own WinForms.

The example tree contains one entry that pulls in the opposite choice: `examples/cef.py`. That file is the place where a Chromium Embedded Framework based renderer gets used, which is the heavyweight path the default design is avoiding. Having it in the tree tells you the option exists, not that it is the default.

The cost of that choice is uniformity. A frozen build is small because it borrows whatever the host system provides, so the same binary behaves differently depending on what the operating system already has.

One author, one process for support, and a security policy file

The project is created by Roman Sirokov, and the README carries a consulting offer from the same person, listing JavaScript and TypeScript, React and Vue, Python, GIS, SQL databases, API integration, CI/CD pipelines, and cloud work, with an EU VAT registration mentioned. Community contributions are still invited, through the contributing page and the contributor graph.

Around that sits ordinary project infrastructure in the tree: `CONTRIBUTING.md`, `SECURITY.md`, `AGENTS.md`, `.pre-commit-config.yaml`, and `tests/` alongside `docs/` and `examples/`. `interop/` is a directory with no README counterpart.

Versioning comes from `setuptools_scm`, with the version marked dynamic in the metadata rather than written by hand. The released tags run 6.1, then 6.2, then 6.2.1, and the last push landed on 2026-10-26, months after the 2026-04-15 tag.

What this wrapper does not take off your hands

Three limits are visible in the packaging rather than in prose.

The native GUI stack is never bundled. WinForms on Windows, Cocoa and WebKit on macOS, Qt or GTK on Linux, all of it assumed present. That is what keeps a frozen build small, and it is also why the install command is followed by a pointer to the installation page.

Rendering is not identical across platforms either. The window is native, but the engine inside it is whatever the platform provides, so a page that behaves one way in Safari may not behave the same way in a Windows webview.

And the interface is yours to build. The package gives you a window, a server, DOM access, and a bridge in both directions. Apart from the native application menu and dialogs pywebview provides, layout and every screen come from the HTML and CSS you write, which is the point for a Python developer who does not want a widget toolkit, and the reason a React boilerplate exists as a separate repository for teams who would rather start from a component setup.

Editorial conclusion

pywebview fits a Python program that already owns its logic and wants the interface written in HTML, CSS, and JavaScript without shipping a browser runtime in the frozen build. It does not fit a team that wants one rendering path on every machine, since macOS gets WebKit through Cocoa, Windows gets WinForms through pythonnet, and Linux gets Qt or GTK, and those are different environments to test. Before you commit, read the installation page for the platform you target, install the native libraries your system is missing, and confirm your Python is 3.10 or newer.

Frequently asked questions

What is pywebview used for?

It is a lightweight native webview wrapper that displays HTML content in its own native GUI window, using WinForms on Windows, Cocoa on macOS, and QT or GTK on Linux, with Android also supported. It ships a built-in HTTP server, DOM support in Python, window management, an event system, and two way communication between JavaScript and Python.

How do you install pywebview?

The documented command is `pip install pywebview`. The README then notes that you might need additional libraries and points to the installation page, which matters because the package metadata declares the bridges conditionally: pythonnet on Windows, a set of pyobjc frameworks on macOS, and QtPy for OpenBSD, plus proxy_tools, bottle, and typing_extensions everywhere.

How do you open a window with pywebview?

Import the module, call `webview.create_window` with a title and a URL, then call `webview.start()`. The full hello world in the README is those three lines, with the URL pointing at a page served elsewhere. The example tree extends this with `examples/change_url.py`, `examples/events.py`, `examples/evaluate_js.py`, and `examples/expose.py`.

How does pywebview compare to Electron?

The README does not make that comparison. What it states about its own approach is that a frozen application does not bundle a heavy GUI toolkit or web renderer, keeping the executable size small, and that it uses the native GUI library of each platform.

Is pywebview maintained and safe to use?

The repository is not archived and the last push was on 2026-10-26, with released versions 6.1, 6.2, and 6.2.1. A `SECURITY.md` file exists at the root, and Python 3.10 or newer is required, with classifiers recorded for 3.10 through 3.13.

Official sources

  1. License: BSD-3-Clause
  2. Project website
  3. r0x0r/pywebview on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/r0x0r-pywebview.svg)](https://hysenlabs.com/projects/r0x0r-pywebview)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/r0x0r-pywebview.svg)](https://hysenlabs.com/projects/r0x0r-pywebview)

Was this useful?