DEV Community

Jose Quintero
Jose Quintero

Posted on

I built a framework that lets you write native apps with YAML and Lua

I've been doing native mobile development for a while and one thing that always bugged me was the tradeoff between cross-platform tools and actual native UI. Flutter and React Native solve the "write once" problem but you're not really getting native components. And writing everything twice in Swift and Kotlin is just exhausting.

So I built Melody. You define your UI in YAML, write your logic in Lua, and it renders real SwiftUI on Apple platforms and Jetpack Compose on Android. No web views, no bridge, no JS runtime.

Here's what a full screen looks like:

screens:
  - id: home
    path: /
    title: Home
    state:
      count: 0

    body:
      - component: text
        text: "{{ 'Tapped ' .. state.count .. ' times' }}"
        style:
          fontSize: 24
          fontWeight: bold

      - component: button
        label: Tap me
        onTap: |
          state.count = state.count + 1
Enter fullscreen mode Exit fullscreen mode

That's it. Change the YAML, hot reload picks it up over WebSocket, and the app updates instantly.

Why YAML and Lua?

YAML handles the layout. It's declarative, easy to read, and easy to diff in git. Lua handles the logic — it's tiny, fast, and embeddable. No npm, no bundler, no node_modules. The whole dev experience is just editing files and saving them.

What's in the box

There are 23+ built-in components that map directly to native views — text, buttons, stacks, lists, grids, forms, charts, toggles, pickers, sliders, inputs, and more. Styling is inline in a style block with support for padding, colors, shadows, border radius, animations, etc. You reference theme colors with "theme.colorName" and they resolve automatically including dark mode.

State is reactive. You assign to state.key in Lua and only the components that reference that key re-render. No diffing, no virtual DOM.

Networking is built in with melody.fetch() which is non-blocking under the hood using coroutines, so your Lua reads linearly but doesn't freeze the UI. There's also WebSocket support, persistence, cross-screen events, timers, and clipboard access.

Navigation

Navigation is path-based with dynamic route params:

- id: profile
  path: /profile/:id
  onMount: |
    local res = melody.fetch("https://api.example.com/user/" .. params.id)
    if res.ok then state.user = res.data end
Enter fullscreen mode Exit fullscreen mode

Tab bars, sheets, alerts, and full navigation stacks are all supported. Tabs even adapt between sidebar on iPad/Mac and tab bar on iPhone with a single config.

Custom components

Custom components work like you'd expect — define once in YAML with props, use them anywhere:

components:
  UserCard:
    props:
      name: ""
      avatar: ""
    body:
      - component: stack
        direction: horizontal
        style: { spacing: 12, padding: 16 }
        children:
          - component: image
            src: "{{ props.avatar }}"
            style: { width: 48, height: 48, borderRadius: 24 }
          - component: text
            text: "{{ props.name }}"
Enter fullscreen mode Exit fullscreen mode

Plugins

Plugins let you extend Melody with native code. A plugin is a git repo with Swift and Kotlin implementations that register functions into Lua under their own namespace. You declare them in app.yaml and run melody plugins install. Useful for things like keychain access, analytics, or anything that needs platform APIs.

Getting started

Install the CLI with Homebrew, scaffold a project, and run it:

brew tap josejuanqm/tap
brew install melody
melody create MyApp
cd MyApp
melody dev
Enter fullscreen mode Exit fullscreen mode

It generates the Xcode project and Android boilerplate for you. The dev server handles hot reload.

I'm currently using Melody to build a real app so this isn't just a toy — it's been tested against real use cases with networking, auth flows, dynamic lists, and complex navigation.

It supports iOS, iPadOS, macOS, tvOS, visionOS, and Android.

Web, Windows, and Linux support is planned and I'd love to work on it if theres traction.

The repo is here:

GitHub logo josejuanqm / melody

A declarative UI framework that interprets YAML configuration and Lua scripting into fully native SwiftUI and Jetpack Compose — no web views, no hybrid layers.

Melody

Build native apps with YAML and Lua. One codebase, every Apple platform + Android.

No JSX, no bridge, no bundler — describe your UI in YAML, write your logic in Lua, and Melody renders it as real native UI. SwiftUI on Apple platforms, Jetpack Compose on Android.

app:
  name: MyApp
  theme:
    primary: "#6366f1"

screens:
  - id: home
    path: /
    title: Home
    state:
      count: 0

    body:
      - component: text
        text: "{{ 'Tapped ' .. state.count .. ' times' }}"
        style:
          fontSize: 24
          fontWeight: bold

      - component: button
        label: Tap me
        onTap: |
          state.count = state.count + 1
          melody.log("count is now " .. state.count)
Enter fullscreen mode Exit fullscreen mode

That's a full screen. Change the YAML, hot reload, see it instantly.

Why

I wanted to build apps fast without fighting tooling. React Native…




Would love to hear any feedback or questions.

Top comments (0)