Make any app.
Write it in Python.
pyxpo is an open-source framework for truly native iOS, Android, Mac and Windows apps, and for the web. React-style components, hooks and file-based routing, running on real CPython on the device.
- iOS 13+
- Android 7+ (API 24)
- macOS and Windows
- Web
- CPython 3.13.7
- MIT licensed
from pyxpo import FlatList, Pressable, StyleSheet, Text, View, useState
HABITS = [("Morning run", 12), ("Read 20 pages", 5),
("Drink water", 30), ("Meditate", 3)]
def Today():
done, setDone = useState({"Morning run"})
def row(p):
name, streak = p["item"]
on = name in done
return Pressable(
View(style=[styles.check, on and styles.checked]),
View(
Text(name, style=[styles.name, on and styles.faded]),
Text("%d day streak" % streak, style=styles.muted),
style=styles.info,
),
onPress=lambda: setDone(lambda d: d ^ {name}),
style=styles.row,
)
return View(
Text("Today", style=styles.title),
Text("%d of %d done" % (len(done), len(HABITS)), style=styles.muted),
FlatList(data=HABITS, renderItem=row, keyExtractor=lambda h, i: h[0]),
style=styles.screen,
)
styles = StyleSheet.create(
screen=dict(flex=1, padding=16, gap=8, backgroundColor="#f2f2f7"),
title=dict(fontSize=30, fontWeight="bold"),
row=dict(flexDirection="row", alignItems="center", gap=10,
padding=12, borderRadius=14, backgroundColor="#fff"),
check=dict(width=22, height=22, borderRadius=11,
borderWidth=2, borderColor="#3776ab"),
checked=dict(backgroundColor="#3776ab"),
info=dict(flex=1, gap=2),
name=dict(fontSize=15, fontWeight="600"),
faded=dict(opacity=0.45),
muted=dict(fontSize=12, color="#8a909b"),
)
default = TodayToday
1 of 4 done
Today
1 of 4 done
Live preview: tap a habit on either phone. One Python file, two native apps.
Everything an app needs, in Python.
If you have used React Native or Expo, you already know the shape of pyxpo. The same ideas, written as plain Python functions.
Native components
10+ components render to real UIKit, Android and WinUI views. No web view.
File-based router
Files in app/ become screens. Stack, Tabs and Drawer layouts, dynamic routes and deep links.
Hooks you already know
In camelCase or snake_case.
Hot reload
Save a file and the screen updates, keeping its state.
App config in pyproject
Icons, splash, permissions and deep links. No native files to edit.
Native modules
Device features as pip packages. Only what you list is compiled in.
Real CPython on device
The standard library and pure-Python packages, inside your app.
Themes and StyleSheets
Light and dark themes that follow the system. Switching restyles views without a re-render.
Responsive layout
Breakpoints like CSS media queries, plus Container, Row, Col, Grid and Flex.
Web and desktop
The same app as a static web site on Pyodide, a Mac app and a native Windows app.
From pip install to app binary.
One CLI covers the whole loop. Everything runs on your machine.
pip install pyxpo
pyxpo create demo && cd demo
pyxpo doctor # checks Xcode, Android SDK and GradleDevice features are one pip install away.
Each native feature is its own package. Add it to your project and list it in modules; only the modules you list are compiled into your app.
pyxpo-storageSecure Storepyxpo-secure-storeFile Systempyxpo-file-systemSQLitepyxpo-sqliteImage Pickerpyxpo-image-pickerImage Manipulatorpyxpo-image-manipulatorMedia Librarypyxpo-media-libraryCamerapyxpo-cameraAudiopyxpo-audioDrawing canvaspyxpo-drawing-canvasWhiteboardpyxpo-whiteboardMesh Viewpyxpo-mesh-viewSharingpyxpo-sharingApplicationpyxpo-applicationDevicepyxpo-deviceHapticspyxpo-hapticsLocalizationpyxpo-localizationNetworkpyxpo-networkLocationpyxpo-locationFetchpyxpo-fetchWeb Browserpyxpo-web-browserDot envpyxpo-dot-envNotificationspyxpo-notificationsGoogle Mobile Adspyxpo-google-mobile-adspip install pyxpo-image-pickerfrom pyxpo import Button, Image, View, useState
from pyxpo_image_picker import ImagePicker
def Index():
uri, setUri = useState(None)
async def pick():
res = await ImagePicker.launchImageLibraryAsync()
if not res["canceled"]:
setUri(res["assets"][0]["uri"])
return View(
Button(title="Pick a photo", onPress=pick),
uri and Image(source={"uri": uri}, style={"width": 240, "height": 240}),
)
default = IndexNative speed. Python simplicity.
No web view and no transpiling. Your code runs in CPython on its own thread, and only the changes cross to the native UI.
- hooks
- reconciler
- diff
{"op":"update","id":7,
"props":{"text":"2 of 4 done"}}- UIView
- android.view.View
- native layout
One bridge crossing per commit
State changes are batched and diffed, then sent as one message. Not one native call per widget.
UI thread stays free
Python runs on its own thread. Scrolling, layout and touch handling stay on the native UI thread.
Only what changed
The reconciler matches elements by type and key and sends only the props that differ.
Animations on the native driver
Animated timing and spring run natively by default, so they stay smooth while Python is busy.
Real native widgets
UIKit and Android views with flexbox layout computed natively. No web view, no canvas.
Fast startup, lean builds
The standard library ships precompiled, and release APKs use R8 minify and shrink.
Familiar API. Python style.
Every React Native name works, and so does its snake_case twin. There is no JSX: children are just call arguments, and styles are dicts.
useStateoruse_state,onPressoron_pressStyleSheet.createwith keyword arguments, or a function of the themeasync defhandlers for camera, files and network callsErrorBoundarykeeps one broken screen from crashing the app
from pyxpo import Pressable, StyleSheet, Text, View, component, use_state
styles = StyleSheet.create(
title=dict(fontSize=24, fontWeight="bold"),
box=dict(padding=16, borderRadius=12, backgroundColor="#ffd43b"),
)
@component
def Counter():
count, set_count = use_state(0)
return View(
Text("Hello from Python", style=styles.title),
Pressable(
Text(f"Tapped {count} times"),
on_press=lambda: set_count(count + 1),
style=styles.box,
),
)Your editor knows pyxpo.
The VS Code extension fills in what Pylance cannot see: style keys, component props, colors and your app config.
- Completion for style keys, their values and component props
- Auto-import: pick
Viewandfrom pyxpo import Viewis added - Color swatches, a color picker and checks for unknown style keys
[tool.pyxpo]completion and run commands for iOS, Android, desktop and web
Bring the language you already use.
pyxpo is not a Python flavour of JavaScript. It is CPython, so the tools and libraries you know come with you.
The standard library on the phone
json, sqlite3, datetime, re, pathlib, asyncio and the rest run on the device, because the app ships a real CPython.
Your PyPI packages
Pure-Python packages work as they are. pyxpo add-lib checks whether a package has wheels for iOS and Android before you add it.
One language for app and backend
Share models, validation and business logic with your Python server instead of writing it twice.
Testing with pytest
Render components in memory and test modules without a device, using the test helpers that ship with pyxpo.
Is pyxpo ready for production?
Not yet. The current release is 0.1.0a5, an alpha. The API follows React Native and Expo closely, but it can still change between releases.
Does it use a web view?
Not on phones or desktop. Components render to real UIKit views on iOS and Mac, Android views on Android and XAML controls on Windows, and your Python runs in CPython inside the app. The web target is the one exception: there the app renders to the DOM and Python runs on Pyodide in the page.
Which platforms can I build for?
iOS, Android, Mac (through Mac Catalyst), Windows (WinUI 3) and the web, from one project. A browser preview needs no toolchain at all and is the fastest loop for screen work.
Can I use packages from PyPI?
Pure-Python packages work. Packages with compiled code need wheels built for iOS and Android. Run pyxpo add-lib to check a package first.
What do I need on my computer?
Python, plus Xcode for iOS and Mac builds, the Android SDK for Android builds and Visual Studio for Windows builds. The web target and the browser preview need only Python. pyxpo doctor checks your setup and tells you what is missing.
Can I write my own native module?
Yes. A module is a pip package with Python code, a small JSON manifest and optional Swift and Kotlin sources. The official packages are built the same way.
Build your first app today.
Free and open source. Install it from PyPI and have a screen running in a few minutes.