Skip to content

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:

AdvantageDescription
PrivacyAll data stays on the user's device — no cloud transmission
Offline FirstWorks without internet connection
No API CostsZero ongoing costs for external services
Simple DeploymentDistribute a single file, no server setup
Data OwnershipUsers 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:

yaml
# 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}}"
yaml
# 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:

yaml
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: true

No .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.yaml

Packaging 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.dmg

Pre-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-qrExternal QR APIGenerates QR codes locally
@ha-bits/bit-database-sqlCloud databaseSQLite runs on device
@ha-bits/bit-imageImage processing APILocal image manipulation

2. Handle Data Persistence

For local storage, use one of these bits based on your data model:

BitBest ForExample Use
@ha-bits/bit-database-sqlStructured, relational dataUsers, products, transactions
@ha-bits/bit-database-pouchDocuments, sync-capableNotes, settings, offline-first apps
@ha-bits/bit-fsFiles, binary dataImages, 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

Released under the Apache 2.0 License.