Skip to content

Haskell Codebase for Web, Android and iOS

Bernard Sibanda edited this page Jul 25, 2026 · 2 revisions
image image

Building Cross-Platform Applications with Miso and Lynx

Table of Contents

  1. What You Will Build
  2. What Miso-Lynx Is
  3. Understanding the Architecture
  4. Important Project Status Warning
  5. Development Requirements
  6. Installing the Tools
  7. Downloading Miso-Lynx
  8. Building the Existing Counter Application
  9. Running the Application in Lynx Explorer
  10. Understanding the Haskell Application
  11. Creating Your Own Haskell Application
  12. Building the Lynx Bundle
  13. Running on Android
  14. Running on iOS
  15. Running in a Web Browser
  16. Sharing Code Correctly
  17. Calling APIs
  18. Accessing Native Device Features
  19. Preparing Production Applications
  20. Testing Strategy
  21. Suggested Project Structure
  22. Common Problems
  23. Production Readiness Checklist
  24. Glossary

1. What You Will Build

In this tutorial, you will create an application whose main interface and application logic are written in Haskell.

The same Haskell application can be loaded into:

  • An Android application
  • An iOS application
  • A web application
  • Lynx Explorer during development

The application passes through the following build pipeline:

Haskell source code
        │
        ▼
Miso application
        │
        ▼
JavaScript-targeting Haskell compiler
        │
        ▼
Generated JavaScript
        │
        ▼
Bun minification
        │
        ▼
Rspack and Lynx plugins
        │
        ▼
main.lynx.bundle
        │
        ├── Android Lynx runtime
        ├── iOS Lynx runtime
        └── Lynx for Web

The repository's bundle script builds the Haskell counter, obtains its generated all.js, minifies it with Bun and uses Rspack to produce dist/main.lynx.bundle.


2. What Miso-Lynx Is

Miso-Lynx combines two technologies:

2.1 Miso

Miso is a Haskell frontend framework inspired by Elm and React.

A Miso application normally contains:

  • A model representing the application state
  • Actions representing user events
  • An update function that changes the model
  • A view function that creates the interface

2.2 Lynx

Lynx is a cross-platform rendering engine. It can render interfaces on Android, iOS, HarmonyOS and the web.

Unlike a normal website displayed inside a basic WebView, Lynx can render platform-native interface elements. It also provides a bridge for calling native Swift, Objective-C, Kotlin and Java functionality. (Lynx)

2.3 Miso-Lynx

Miso-Lynx connects Miso's Haskell application architecture to Lynx's rendering engine.

The repository describes the framework as supporting drawing and API interaction on iOS, Android and HarmonyOS. It also identifies web as one of Lynx's target platforms.


3. Understanding the Architecture

A realistic cross-platform project does not contain only one file.

It normally contains three layers.

3.1 Shared Haskell application

This contains most of your application:

Application state
Business rules
Validation
API requests
Navigation state
Authentication state
Interface components
Error handling
Data conversion

3.2 Lynx bundle

Your Haskell application is compiled and packaged as:

main.lynx.bundle

This bundle contains the cross-platform application interface and behaviour.

3.3 Platform hosts

Each operating system still needs a small host application:

Android host  → Kotlin or Java
iOS host      → Swift or Objective-C
Web host      → JavaScript or TypeScript

The host is responsible for:

  • Starting the Lynx runtime
  • Loading main.lynx.bundle
  • Setting the application icon
  • Requesting permissions
  • Signing the application
  • Connecting native services
  • Publishing the application

Therefore, “one codebase” means:

One shared Haskell application with small platform-specific packaging projects.

It does not mean that Android Studio, Xcode and web packaging completely disappear.


4. Important Project Status Warning

Miso-Lynx is currently experimental.

The repository warns that:

  • It is under heavy development.
  • Event handling and drawing currently happen on the main thread.
  • Work on scheduling Haskell across Lynx threads is still ongoing.

There is also some version drift in the repository:

  • cabal.project currently pins Miso 1.12.0.
  • The README still refers to Miso 1.9.
  • The JavaScript dependencies use pre-4.0 Lynx package versions.
  • Current official Lynx documentation describes Lynx 4.0.

For your first build:

  1. Clone the repository.
  2. Keep its existing lockfile.
  3. Do not update all JavaScript packages.
  4. Build the included counter first.
  5. Upgrade Lynx only after the original example works.

5. Development Requirements

5.1 Common tools

Install:

  • Git
  • Nix
  • Bun
  • A code editor
  • Lynx Explorer
  • A recent web browser

The repository's CI currently uses Nix and Bun. Its active CI build tests the native Haskell library, while the JavaScript example and bundle steps are presently commented out.

5.2 Android development

For Android development, install:

  • Android Studio
  • Android SDK
  • Java Development Kit
  • Android emulator or physical Android phone
  • Lynx Explorer

5.3 iOS development

For iOS development, you need:

  • A Mac
  • Xcode
  • An iOS simulator or iPhone
  • CocoaPods when creating a production host
  • An Apple Developer account for App Store distribution

You cannot produce a normal signed iOS application with Xcode directly from Ubuntu or Windows.

5.4 Web development

For web development, install:

  • Node.js or Bun
  • Rsbuild or another supported web build tool
  • @lynx-js/web-core

Current Lynx documentation requires Node.js 20.19 or later, or a supported Node.js 22 release, for its current development tooling. (Lynx)


6. Installing the Tools

6.1 Install Nix

Follow the official Nix installation instructions for your operating system.

After installing Nix, restart the terminal and test it:

nix --version

6.2 Install Bun

Test whether Bun is already installed:

bun --version

6.3 Enable the Miso binary cache

The repository recommends using its Cachix binary cache because compiling the complete Haskell JavaScript toolchain locally can be slow.

After installing Cachix, run:

cachix use haskell-miso-cachix

7. Downloading Miso-Lynx

Clone the repository:

git clone https://github.com/haskell-miso/miso-lynx.git
cd miso-lynx

Confirm the important files:

ls

You should see files such as:

README.md
miso-lynx.cabal
cabal.project
package.json
rspack.config.js
default.nix
shell.nix
examples/
src/
ts/
nix/

Install the JavaScript dependencies:

bun install

Do not delete bun.lock. It helps keep the JavaScript dependency versions stable.


8. Building the Existing Counter Application

The safest starting point is the included counter application.

Its Haskell source is located at:

examples/counter/Main.hs

Its Cabal executable is defined in:

examples/miso-native-examples.cabal

The executable depends on:

base
containers
miso
miso-lynx

The example is configured as the counter executable.

8.1 Build the bundle script

From the repository root, run:

nix-build -A counter-bundle

This should create:

result/bin/counter-bundle

8.2 Run the bundle script

Run:

./result/bin/counter-bundle

The script performs approximately these operations:

1. Clean the previous build.
2. Create the dist directory.
3. Build the Miso-Lynx JavaScript support library.
4. Compile the Haskell counter.
5. Copy the generated all.js file.
6. Minify the generated JavaScript.
7. Run Rspack.
8. Create main.lynx.bundle.

Check the result:

ls -lh dist

You should see:

main.lynx.bundle

The Rspack configuration names this output main.lynx.bundle.


9. Running the Application in Lynx Explorer

Lynx Explorer is the easiest development host.

It lets you test a Lynx bundle without first creating a complete Android or iOS application.

9.1 Start a local server

Run:

bunx http-server dist -p 8080 --cors -c-1

Your bundle should now be available at:

http://localhost:8080/main.lynx.bundle

9.2 Find your computer's network address

On Linux:

hostname -I

Suppose the result is:

192.168.1.30

The phone should use:

http://192.168.1.30:8080/main.lynx.bundle

Do not use localhost on a physical phone. On the phone, localhost means the phone itself.

9.3 Open the bundle

In Lynx Explorer:

  1. Open the bundle URL field.
  2. Enter the complete URL.
  3. Select Go.
  4. Wait for the bundle to load.

The official Lynx workflow similarly uses Lynx Explorer to load a development bundle URL or scan a generated QR code. (Lynx)


10. Understanding the Haskell Application

The existing counter demonstrates the standard Miso architecture.

10.1 Model

The model stores the application state:

newtype Model = Model
  { _value :: Int
  }

For a larger application, the model could contain:

data Model = Model
  { currentUser :: Maybe User
  , products    :: [Product]
  , loading     :: Bool
  , errorText   :: Maybe MisoString
  , currentPage :: Page
  }

10.2 Actions

Actions describe events:

data Action
  = AddOne
  | SubtractOne

A real application might use:

data Action
  = StartApplication
  | OpenLogin
  | UpdateEmail MisoString
  | UpdatePassword MisoString
  | SubmitLogin
  | LoginCompleted User
  | LoginFailed MisoString
  | OpenDashboard
  | Logout

10.3 Update function

The update function handles each action and changes the model:

updateModel = \case
  AddOne ->
    value += 1

  SubtractOne ->
    value -= 1

This separates application behaviour from interface drawing.

10.4 View function

The view function converts the model into an interface:

viewModel :: props -> Model -> View Model Action

When the model changes, Miso calculates the required interface changes.

10.5 Lynx entry point

A normal browser-based Miso application and a Miso-Lynx application have different entry points.

Miso-Lynx uses:

main :: IO ()
main = lynx lynxEvents application

The lynx function renders the Miso application into the Lynx "native" root.


11. Creating Your Own Haskell Application

Begin by modifying the existing counter instead of immediately creating a completely separate build system.

Replace examples/counter/Main.hs with the following simple score application:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

module Main where

import Miso hiding (text_)
import Miso.Lens
import Miso.Lynx
import Miso.Lynx.Element.View.Event (onTap)
import Miso.String
import qualified Miso.CSS as CSS

newtype Model = Model
  { _score :: Int
  }
  deriving (Show, Eq, ToMisoString)

score :: Lens Model Int
score = lens _score $ \model newScore ->
  model { _score = newScore }

data Action
  = Increase
  | Decrease
  deriving (Show, Eq)

main :: IO ()
main =
  lynx lynxEvents scoreApp

scoreApp :: App Model Action
scoreApp =
  component
    (Model 0)
    updateModel
    viewModel

updateModel
  :: Action
  -> Effect ROOT props Model Action
updateModel = \case
  Increase ->
    score += 1

  Decrease ->
    score -= 1

viewModel
  :: props
  -> Model
  -> View Model Action
viewModel _ model =
  view_
    [ CSS.style_
        [ CSS.height "300px"
        , CSS.display "flex"
        , CSS.alignItems "center"
        , CSS.justifyContent "center"
        ]
    ]
    [ scoreButton "−" Decrease
    , view_
        [ CSS.style_
            [ CSS.width "120px"
            , CSS.height "100px"
            , CSS.margin "8px"
            , CSS.display "flex"
            , CSS.alignItems "center"
            , CSS.justifyContent "center"
            , CSS.backgroundColor CSS.orange
            ]
        ]
        [ text_
            [ CSS.style_
                [ CSS.fontSize "42px"
                ]
            ]
            [ text $ ms (_score model)
            ]
        ]
    , scoreButton "+" Increase
    ]

scoreButton label action =
  view_
    [ onTap action
    , CSS.style_
        [ CSS.width "90px"
        , CSS.height "90px"
        , CSS.margin "8px"
        , CSS.display "flex"
        , CSS.alignItems "center"
        , CSS.justifyContent "center"
        , CSS.backgroundColor CSS.yellow
        ]
    ]
    [ text_
        [ CSS.style_
            [ CSS.fontSize "40px"
            ]
        ]
        [ label
        ]
    ]

This application still has the four central parts:

Model       → score value
Action      → Increase or Decrease
Update      → change the score
View        → display buttons and score

12. Building the Lynx Bundle

After changing Main.hs, rebuild:

./result/bin/counter-bundle

Restart the server when necessary:

bunx http-server dist -p 8080 --cors -c-1

Reload the bundle in Lynx Explorer.

During early development, this is the basic cycle:

Edit Haskell
    ↓
Compile Haskell
    ↓
Build main.lynx.bundle
    ↓
Reload Lynx Explorer
    ↓
Test

The repository discusses browser-based GHC hot reload, but identifies that integration as continuing work rather than a complete beginner workflow.


13. Running on Android

There are two Android stages.

13.1 Development stage: Lynx Explorer

This is the fastest approach.

  1. Install Lynx Explorer on an Android phone or emulator.
  2. Start the local bundle server.
  3. Ensure the phone and computer are on the same network.
  4. Enter the bundle URL.
  5. Test the application.

For an Android emulator, try:

http://10.0.2.2:8080/main.lynx.bundle

For a physical phone, use the computer's local network address:

http://192.168.1.30:8080/main.lynx.bundle

13.2 Production stage: Android host

For a distributable application:

  1. Create an Android Studio project.
  2. Add the Lynx Android dependencies.
  3. Initialise Lynx in the Android Application class.
  4. Add a LynxView to an Activity.
  5. Copy main.lynx.bundle into the Android assets.
  6. Load the bundle through the Android resource loader.
  7. Add application permissions.
  8. Create a signed Android App Bundle.
  9. Upload the .aab file to Google Play Console.

Current official Lynx documentation provides Android libraries for the engine, JavaScript runtime, tracing and PrimJS. It also requires host-side initialisation and a LynxView to render the bundle. (Lynx)

Version warning

Do not automatically combine the repository's old Lynx build packages with the latest Android Lynx runtime.

The bundle compiler and runtime should use compatible Lynx versions. Upgrade the bundler and Android runtime together.


14. Running on iOS

14.1 Development stage: iOS simulator

On a Mac:

  1. Install Xcode.
  2. Start an iOS simulator.
  3. Install the prebuilt Lynx Explorer application in the simulator.
  4. Start the local bundle server.
  5. Load:
http://127.0.0.1:8080/main.lynx.bundle

The official documentation currently provides a prebuilt Lynx Explorer for the iOS simulator. A real-device build requires building or packaging an iOS host. (Lynx)

14.2 Production stage: iOS host

For an App Store application:

  1. Create an Xcode application.
  2. Add the compatible Lynx libraries through CocoaPods.
  3. Initialise LynxEnv.
  4. Create a LynxView.
  5. Add main.lynx.bundle to Copy Bundle Resources.
  6. Implement a bundle provider.
  7. Load the bundle into LynxView.
  8. Add icons, permissions and launch screens.
  9. Configure signing.
  10. Archive the application.
  11. Upload it through Xcode or Transporter.

The official iOS integration uses a native LynxView, a template provider and a bundle embedded in the application resources. (Lynx)


15. Running in a Web Browser

Lynx for Web implements the Lynx engine inside a browser. It can be embedded into React, Vue, Svelte or another web project. (Lynx)

15.1 Important repository limitation

The repository's present Rspack configuration creates:

main.lynx.bundle

Current Lynx web tooling normally creates a web-specific artifact such as:

main.web.bundle

Therefore, the web target needs an additional web build configuration.

15.2 Create a small web host

Create the web project beside the Miso-Lynx project:

cd ..
npm create rsbuild@latest web-host
cd web-host
npm install
npm install @lynx-js/web-core

Add the Lynx client to your application:

import '@lynx-js/web-core/client';

export default function App() {
  return (
    <lynx-view
      style={{
        width: '100vw',
        height: '100vh'
      }}
      url="/main.web.bundle"
    />
  );
}

Copy the web bundle into the web project's public directory:

web-host/public/main.web.bundle

Run the web host:

npm run dev

Open:

http://localhost:3000

The official web integration uses the lynx-view custom element from @lynx-js/web-core to load a web bundle. (Lynx)

15.3 Alternative web strategy

Until the Miso-Lynx web bundle pipeline is fully configured, you can share:

  • Models
  • Actions
  • Update functions
  • Validation
  • API clients
  • Business rules

Then use:

Miso.Lynx views for mobile
Standard Miso HTML views for browsers

This approach shares almost all application logic but uses separate thin view modules.

It is often more reliable than forcing experimental mobile rendering into a production website.


16. Sharing Code Correctly

A scalable application should separate reusable logic from platform integration.

16.1 Shared domain module

module App.Domain where

data User = User
  { userId   :: Int
  , userName :: Text
  }

data LoginError
  = InvalidEmail
  | InvalidPassword
  | NetworkFailure

16.2 Shared validation module

module App.Validation where

validEmail :: Text -> Bool
validEmail email =
  "@" `Text.isInfixOf` email

16.3 Shared model module

module App.Model where

data Model = Model
  { currentUser :: Maybe User
  , loading     :: Bool
  , errorText   :: Maybe Text
  }

16.4 Shared update module

module App.Update where

data Action
  = LoginRequested
  | LoginSucceeded User
  | LoginFailed Text

16.5 Platform modules

App.View.Lynx
App.View.Web
App.Native.Camera
App.Native.Storage
App.Native.Notifications

Only native integrations should depend directly on Android, iOS or browser-specific behaviour.


17. Calling APIs

Your application should communicate with a backend through HTTPS APIs.

A common architecture is:

Haskell application
        │
        ▼
HTTPS request
        │
        ▼
PHP, Haskell, Node.js or another backend
        │
        ▼
MySQL or PostgreSQL database

Do not connect a mobile application directly to MySQL.

The application should call endpoints such as:

POST /api/login
GET  /api/profile
GET  /api/products
POST /api/orders

The backend should handle:

  • Authentication
  • Database passwords
  • SQL queries
  • Access control
  • Rate limiting
  • Audit logs
  • Server-side validation

Store API base addresses in configuration rather than hard-coding them throughout the Haskell source.


18. Accessing Native Device Features

Some features require native modules:

  • Camera
  • Fingerprint or Face ID
  • Secure storage
  • Push notifications
  • GPS
  • Bluetooth
  • File selection
  • Sharing
  • Background services
  • Contacts
  • QR scanning

Miso-Lynx itself cannot automatically provide every native service.

The architecture becomes:

Haskell action
      │
      ▼
JavaScript foreign-function call
      │
      ▼
Lynx native module
      │
      ├── Kotlin or Java on Android
      └── Swift or Objective-C on iOS

Lynx supports custom native components and modules written in Swift, Objective-C, Kotlin or Java.

Keep native module interfaces small.

For example:

camera.open()
secureStorage.save(key, value)
notifications.requestPermission()
device.getLocation()

Do not place your main business rules inside the Swift or Kotlin host.


19. Preparing Production Applications

19.1 Android

A production Android build needs:

Application ID
Version code
Version name
App icons
Permissions
Privacy policy
Signing key
Release build
AAB package
Play Console listing

19.2 iOS

A production iOS build needs:

Bundle identifier
Version
Build number
App icons
Permission descriptions
Signing certificate
Provisioning profile
Privacy manifest
Archived IPA
App Store Connect listing

19.3 Web

A production web application needs:

Production bundle
HTTPS hosting
Correct MIME types
Cache headers
Content Security Policy
Error monitoring
Responsive layout
Accessible controls

19.4 Bundle delivery

You can distribute a Lynx bundle in two ways.

Embedded bundle

The bundle is packaged inside the Android or iOS application.

Advantages:

  • Works offline
  • Predictable version
  • Fast startup
  • Easier security review

Remotely loaded bundle

The application downloads the bundle from your server.

Advantages:

  • Easier content updates
  • Smaller native releases
  • Faster interface deployments

Risks:

  • Requires strong bundle verification
  • Requires rollback support
  • Requires secure HTTPS
  • May be restricted by app-store policies
  • A broken remote bundle can affect all users

For a first production application, use an embedded bundle.


20. Testing Strategy

20.1 Test pure Haskell logic

Pure functions should be tested without Android, iOS or Lynx.

Examples include:

Email validation
Price calculations
State transitions
Form validation
Permission rules
Data conversion
Date calculations

20.2 Test update behaviour

Verify that actions create the expected state:

LoginRequested
    → loading becomes True

LoginSucceeded
    → user is stored
    → loading becomes False

LoginFailed
    → error is stored
    → loading becomes False

20.3 Test the bundle

Confirm that:

  • The bundle builds.
  • The bundle can be served.
  • Lynx Explorer loads it.
  • Tap events work.
  • Lists scroll correctly.
  • API errors are displayed.

20.4 Test each platform

Test separately on:

  • Small Android phone
  • Large Android phone
  • Android emulator
  • Small iPhone
  • Large iPhone
  • iOS simulator
  • Chrome
  • Firefox
  • Safari

A shared codebase does not remove the need for platform testing.


21. Suggested Project Structure

Use a structure similar to:

coxy-cross-platform/
│
├── haskell/
│   ├── app/
│   │   └── Main.hs
│   │
│   ├── src/
│   │   ├── App/
│   │   │   ├── Model.hs
│   │   │   ├── Action.hs
│   │   │   ├── Update.hs
│   │   │   ├── Validation.hs
│   │   │   ├── Api.hs
│   │   │   ├── Config.hs
│   │   │   └── View/
│   │   │       ├── Lynx.hs
│   │   │       └── Web.hs
│   │   └── Domain/
│   │       ├── User.hs
│   │       └── Product.hs
│   │
│   └── test/
│       ├── ValidationSpec.hs
│       └── UpdateSpec.hs
│
├── dist/
│   ├── main.lynx.bundle
│   └── main.web.bundle
│
├── hosts/
│   ├── android/
│   ├── ios/
│   └── web/
│
├── scripts/
│   ├── build-haskell.sh
│   ├── build-lynx.sh
│   └── copy-bundles.sh
│
├── package.json
├── rspack.config.js
├── cabal.project
└── README.md

This clearly separates:

Shared Haskell application
Generated bundles
Platform packaging
Build scripts

22. Common Problems

Problem One: The build downloads many packages

Cause:

The Haskell JavaScript toolchain is large.

Solution:

cachix use haskell-miso-cachix

Then rebuild.


Problem Two: main.lynx.bundle is missing

Check:

ls -lh dist

Run:

bun install
nix-build -A counter-bundle
./result/bin/counter-bundle

Look for errors before the Rspack step.


Problem Three: The phone cannot connect

Do not use:

http://localhost:8080

Use your computer's network address:

http://192.168.1.30:8080

Also check:

  • Firewall settings
  • Wi-Fi network
  • Port 8080
  • VPN settings
  • Whether the server is still running

Problem Four: The application displays a blank screen

Possible causes:

  • Incorrect bundle URL
  • Bundle build failure
  • JavaScript exception
  • Unsupported element
  • Lynx runtime and bundle version mismatch
  • Missing native service
  • Incorrect Rspack output

Test the original counter again before debugging your larger application.


Problem Five: The build breaks after updating packages

Restore:

bun.lock
package.json
cabal.project

Do not independently update only the Lynx Android runtime or only the JavaScript bundler.

Treat these as one compatibility group:

Lynx compiler packages
Lynx template plugin
Lynx runtime
PrimJS
Lynx services
Web runtime

Problem Six: Normal GHC does not generate the required application

A normal native GHC installation cannot simply switch targets and produce WebAssembly or JavaScript.

GHC cross-target builds are separate compilers. The official GHC WebAssembly documentation explains that stock GHC cannot generate Wasm merely through a compiler option; a Wasm-targeting GHC build is required. ([ghc.gitlab.haskell.org]4)

Use the compiler environment supplied by the repository's Nix configuration.


Problem Seven: iOS cannot be compiled on Ubuntu

The Haskell bundle can be developed on Linux, but the final iOS host, signing and App Store archive require Xcode on macOS.

Use one of these:

  • A physical Mac
  • A company Mac build machine
  • A properly licensed macOS CI runner

23. Production Readiness Checklist

Before publishing, confirm the following.

Shared Haskell application

  • Models are separated from views.
  • Update functions are testable.
  • API errors are handled.
  • Authentication tokens are protected.
  • Input is validated.
  • Loading states are shown.
  • Offline failures are handled.
  • Logs do not expose private information.

Android

  • The correct Lynx runtime is used.
  • The application is signed.
  • Permissions are minimal.
  • The release AAB is tested.
  • Back-button behaviour works.
  • Different screen sizes are tested.

iOS

  • The correct Lynx runtime is used.
  • Signing is configured.
  • Permission descriptions are present.
  • Safe areas are handled.
  • Different iPhone sizes are tested.
  • App Store privacy information is complete.

Web

  • A web-specific bundle is produced.
  • HTTPS is enabled.
  • Browser routing works.
  • Responsive layout is tested.
  • Accessibility is checked.
  • Cache invalidation is configured.

Security

  • No database password is included in the application.
  • No private API secret is included in the bundle.
  • HTTPS is required.
  • Tokens can be revoked.
  • Native bridge calls validate their arguments.
  • Remote bundles are signed or verified.
  • Dependencies are pinned and reviewed.

24. Glossary

Action A value describing something that happened, such as a button tap or completed API request.

Android host The Kotlin or Java application that starts Lynx and loads the shared bundle.

Bundle A compiled application package loaded by the Lynx runtime.

Cabal A Haskell package and build tool.

Effect A Miso value describing a model update that may also perform external work.

FFI Foreign Function Interface. It allows Haskell to communicate with JavaScript or native code.

GHC The Glasgow Haskell Compiler.

Haskell model The value containing the current application state.

iOS host The Swift or Objective-C application that starts Lynx and loads the shared bundle.

Lynx Explorer A development application that loads and displays Lynx bundles.

LynxView The native view used by Android, iOS or web hosts to render a Lynx application.

Miso A Haskell frontend framework based on models, actions, update functions and views.

Native module Platform code that exposes Android or iOS functionality to the shared application.

Nix A reproducible package and build system used by the Miso-Lynx repository.

PrimJS A JavaScript runtime used by Lynx.

Rspack A JavaScript bundler used to package the generated application.

View function A function that converts the current model into interface elements.

Web host A small browser application that loads and displays the Lynx web bundle.


Recommended Learning Order

Follow this order:

1. Build the original counter.
2. Run it in Lynx Explorer.
3. Change its text and colours.
4. Add another model field.
5. Add another action.
6. Call a public test API.
7. Split the application into modules.
8. Test on Android.
9. Test on the iOS simulator.
10. Configure Lynx for Web.
11. Create production Android and iOS hosts.
12. Add native modules only when necessary.

The most important first milestone is:

Successfully produce dist/main.lynx.bundle and load it in Lynx Explorer before creating Android Studio, Xcode or web packaging projects.

This tutorial can next be converted into a navigable single-page HTML course with copy buttons, progress tracking and platform tabs.

Clone this wiki locally