Build a Fully Standalone App (No External APIs)
This guide walks you through building a complete offline-capable application that requires no external API keys or cloud services to function. Everything runs locally on the user's device.
Complete Example
This tutorial uses the QR Code Manager showcase, an app demonstrating local-only workflows with SQLite storage and custom frontend.
Why Build Standalone?
Standalone applications offer significant advantages:
| Advantage | Description |
|---|---|
| Privacy | All data stays on the user's device — no cloud transmission |
| Offline First | Works without internet connection |
| No API Costs | Zero ongoing costs for external services |
| Simple Deployment | Distribute a single file, no server setup |
| Data Ownership | Users control their own data completely |
What You'll Build
The QR Code Manager demonstrates building a multi-habit application entirely with local bits:
- 5 local workflows — Generate QR codes, scan images, full CRUD operations
- SQLite database — Persistent local storage using
@ha-bits/bit-database-sql - QR processing — Generate and decode using
@ha-bits/bit-qr - Custom frontend — Mobile-friendly UI
- Cross-platform packaging — Build for macOS, Windows, Linux, and Android
Workflow Visualization
Explore the workflows that power the QR Code Manager:
Anatomy of a Local-Only Habit
The key to building standalone apps is using local bits instead of API-dependent ones. The Create QR workflow demonstrates this pattern — it uses @ha-bits/bit-qr for local QR generation and @ha-bits/bit-database-sql for local SQLite storage:
# Create QR Code workflow
# Generates a QR code from user input and stores it in the database
id: create-qr
name: Create QR Code
nodes:
# Generate the QR code directly
- id: generate-qr
type: bit
data:
framework: bits
module: "@ha-bits/bit-qr"
operation: generate
source: npm
params:
data: "{{habits.input.data}}"
dataType: "{{habits.input.dataType}}"
format: dataUrl
size: 256
errorCorrection: M
# Store in database
- id: store-qr
type: bit
data:
framework: bits
module: "@ha-bits/bit-database-sql"
operation: insert
source: npm
params:
collection: qr_codes
document:
label: "{{habits.input.label}}"
dataType: "{{habits.input.dataType}}"
content: "{{habits.input.data}}"
image: "{{generate-qr.data}}"
edges:
- source: generate-qr
target: store-qr
output:
success: true
id: "{{store-qr.id}}"
label: "{{habits.input.label}}"
dataType: "{{habits.input.dataType}}"
content: "{{habits.input.data}}"
image: "{{generate-qr.data}}"# List QR Codes workflow
# Returns all stored QR codes from the database
id: list-qrs
name: List QR Codes
nodes:
# Query all QR codes
- id: query-qrs
type: bit
data:
framework: bits
module: "@ha-bits/bit-database-sql"
operation: query
source: npm
params:
collection: qr_codes
filter: "{}"
limit: 100
edges: []
output:
success: true
count: "{{query-qrs.count}}"
items: "{{query-qrs.results}}"Stack Configuration
The stack.yaml for a standalone app references local workflows and configures the server with a custom frontend:
version: "1.0"
workflows:
- id: create-qr
path: ./create-qr.yaml
enabled: true
- id: scan-qr
path: ./scan-qr.yaml
enabled: true
- id: list-qrs
path: ./list-qrs.yaml
enabled: true
- id: get-qr
path: ./get-qr.yaml
enabled: true
- id: delete-qr
path: ./delete-qr.yaml
enabled: true
server:
port: 13000
host: "0.0.0.0"
frontend: ./frontend
logging:
level: info
outputs: [console]
format: text
colorize: trueNo .env Required
Since there are no external API keys, you don't need a .env file. The app works out of the box.
Running Locally
Test your standalone app during development using the ExampleRunner:
Run using the Habits CLI wrapper, recommended if you develop local Habits
# First, download the example files
npx habits@latest cortex --config ./qr-database/stack.yamlPackaging for Distribution
Package your standalone app for any platform:
Standalone macOS app with embedded backend. No server required.
npx habits pack --config ./stack.yaml --format desktop-full --desktop-platform dmg --output ./QRManager.dmgPre-built Downloads
The QR Code Manager showcase includes pre-built binaries you can download and test immediately:
Best Practices for Standalone Apps
1. Choose Local Bits First
When designing workflows, prefer local bits over API-dependent ones:
| Use This (Local) | Instead Of (API) | Why |
|---|---|---|
@ha-bits/bit-qr | External QR API | Generates QR codes locally |
@ha-bits/bit-database-sql | Cloud database | SQLite runs on device |
@ha-bits/bit-image | Image processing API | Local image manipulation |
2. Handle Data Persistence
For local storage, use one of these bits based on your data model:
| Bit | Best For | Example Use |
|---|---|---|
@ha-bits/bit-database-sql | Structured, relational data | Users, products, transactions |
@ha-bits/bit-database-pouch | Documents, sync-capable | Notes, settings, offline-first apps |
@ha-bits/bit-fs | Files, binary data | Images, exports, user uploads |
See the list-qrs.yaml workflow above for a real example of SQLite queries.
3. Design for Offline
- Store all necessary data locally
- Don't depend on external resources (fonts, images, APIs)
- Include fallback UI states for empty data
4. Keep Binary Size Reasonable
Standalone binaries bundle the entire runtime. Keep them lean by:
- Avoiding unnecessary dependencies
- Using optimized frontend assets
- Compressing images before bundling
Next Steps
- QR Code Manager Showcase — Explore the full example
- Packing and Distribution — Learn all packaging options
- Bits Catalog — Discover available local and API bits
- Build AI-Powered App — For apps that need AI capabilities
