⌘K

Introduction

Ship native app screens without waiting for a store review.

Let's discover Nativeblocks in less than 5 minutes.

What is Nativeblocks?

Nativeblocks is a code-push platform for Android and iOS teams. You write screens in Kotlin or Swift using the Nativeblocks DSL, deploy them from the CLI, and users pick up the change on their next app open, no App Store or Play Store review required.

The platform has three main parts:

  • SDK: a lightweight library (under 1 MB) you drop into your existing Android or iOS project. It integrates with Jetpack Compose and SwiftUI, and works with your existing components.
  • CLI: a command-line tool that initializes your project, generates a typed DSL from your registered components, and deploys frames to production.
  • Dashboard: where you manage releases, monitor tag distribution, track daily active users, and roll back to any previous version with one click.

Core concepts

Blocks

A Block is any native UI component annotated with @NativeBlock (Kotlin) or the equivalent macro (Swift).

Once annotated, nativeblocks code-gen generates a typed DSL wrapper so you can compose it in a Frame. See Block & Action for the full annotation reference.

Actions

An Action is a reusable piece of business logic annotated with @NativeAction. Actions are attached to block events in a frame and run when that event fires. Multiple actions on the same event slot run top to bottom.

There is also a built-in script action for inline logic, reading and writing frame variables, without needing a separate class. See Block & Action for details.

Frames

A Frame is a screen defined entirely in the Nativeblocks DSL. It declares variables, optional lifecycle hooks, and a tree of Blocks wired to Actions. Frames are what you version and deploy.

Code Push

When you run nativeblocks frame deploy --tag v1.2.0, the compiled frame is uploaded to the Nativeblocks cloud. The next time a user opens the app, the SDK fetches the latest active frame for each route and renders it. No rebuild, no resubmission.

You can target rollouts by user, app version, or percentage, and roll back to any previous tag instantly from the dashboard.

Sample DSL

A complete frame combining variables, a Block, and an Action:

Android

welcome-android.kt
package com.example.app.frames

import com.example.app.generated.shared.*
import com.example.app.generated.integration.android.Script.script
import com.example.app.generated.integration.android.nativeblocks.column.NativeblocksColumn
import com.example.app.generated.integration.android.nativeblocks.column.nativeblocksColumn
import com.example.app.generated.integration.android.nativeblocks.text.nativeblocksText
import com.example.app.generated.integration.android.nativeblocks.button.nativeblocksButton

fun frame(): NativeblocksFrame = androidFrame(name = "Welcome", route = "/welcome") {
    val greeting    by remember("Hello, world!")
    val count       by remember(0)
    val buttonLabel by remember("Tap me")

    rootBlock {
        nativeblocksColumn(
            blockKey = "root",
            width = NativeblocksColumn.WidthOptions.Match,
            horizontalAlignment = NativeblocksColumn.HorizontalAlignmentOptions.Center,
            content = {
                nativeblocksText(blockKey = "heading", text = greeting, fontSize = "24")
                nativeblocksText(blockKey = "counter", text = count, fontSize = "16")
                nativeblocksButton(
                    blockKey = "incrementBtn",
                    label = buttonLabel,
                    onClick = {
                        script { getVariable, _, _ ->
                            """updateVariable("${count.key}", String(Number(${getVariable(count)}) + 1));"""
                        }
                    },
                )
            },
        )
    }
}

iOS

welcome-ios.kt
package com.example.app.frames

import com.example.app.generated.shared.*
import com.example.app.generated.integration.ios.Script.script
import com.example.app.generated.integration.ios.nativeblocks.vstack.NativeblocksVstack
import com.example.app.generated.integration.ios.nativeblocks.vstack.nativeblocksVstack
import com.example.app.generated.integration.ios.nativeblocks.text.nativeblocksText
import com.example.app.generated.integration.ios.nativeblocks.button.nativeblocksButton

fun frame(): NativeblocksFrame = iosFrame(name = "Welcome", route = "/welcome") {
    val greeting    by remember("Hello, world!")
    val count       by remember(0)
    val buttonLabel by remember("Tap me")

    rootBlock {
        nativeblocksVstack(
            blockKey = "root",
            width = NativeblocksVstack.WidthOptions.Fill,
            alignmentHorizontal = NativeblocksVstack.AlignmentHorizontalOptions.Center,
            content = {
                nativeblocksText(blockKey = "heading", text = greeting, fontSize = "24")
                nativeblocksText(blockKey = "counter", text = count, fontSize = "16")
                nativeblocksButton(
                    blockKey = "incrementBtn",
                    label = buttonLabel,
                    onClick = {
                        script { getVariable, _, _ ->
                            """updateVariable("${count.key}", String(Number(${getVariable(count)}) + 1));"""
                        }
                    },
                )
            },
        )
    }
}

How it works

  1. Add the SDK to your Android or iOS project.
  2. Annotate your components with @NativeBlock and @NativeAction and sync them with Nativeblocks servers.
  3. Run nativeblocks init to authenticate and configure your project and generate the typed DSL.
  4. Write screens using the DSL and deploy with nativeblocks frame deploy.

From your terminal to every user's phone. On every future deploy, users pick it up on their next app open.