Your First Composable

With the module configured in the previous lesson, it's time to write actual Compose code. A "composable" sounds like a special kind of object, but it's really just an ordinary Kotlin function with one extra annotation. This lesson writes the smallest possible one, then wires it all the way through to a running screen.

The @Composable Annotation

@Composable marks a function as one that emits UI rather than returning a value. It changes nothing about how you call the function syntactically — no parentheses tricks, no special return type — but it tells the Compose compiler plugin to instrument the function so it can be tracked, re-invoked, and skipped intelligently during recomposition (the subject of a later lesson).

@Composable
fun Greeting(name: String) {
    Text(text = "Hello, $name!")
}

A few rules fall directly out of what this annotation does. A @Composable function can only be called from inside another @Composable function (or a designated entry point like setContent { }) — the compiler enforces this, because the special tracking machinery only exists inside that call context. A composable function also conventionally returns Unit (nothing) — instead of returning a UI object, it emits UI as a side effect of running, by calling other composables like Text, Column, and Button.

A Composable Is Just a Function That Emits UI

It helps to think of every composable — even built-in ones like Text — as doing one thing: describing a piece of UI by calling other, smaller composables, all the way down to a handful of true primitives that Compose's rendering layer understands directly. Your own composables are no different in kind from Text or Column — they're just user-defined functions in that same chain:

@Composable
fun RecipeBoxApp() {
    // RecipeBoxApp emits UI by calling other composables
    Greeting(name = "RecipeBox")
}

This composition chain — one composable calling others, which call others in turn — is called the composition. Compose walks this chain, and every composable's job is simply to describe its own small piece of it.

Wiring It Up: setContent { RecipeBoxApp() }

The previous lesson introduced setContent { } as the bridge between an Activity and the Compose world. Putting the pieces together, here's the complete, minimal "Hello RecipeBox" app — a real MainActivity hosting a real composable, nothing skipped:

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            RecipeBoxApp()
        }
    }
}

@Composable
fun RecipeBoxApp() {
    Greeting(name = "RecipeBox")
}

@Composable
fun Greeting(name: String) {
    Text(text = "Hello, $name!")
}

Reading this top to bottom: when MainActivity starts, onCreate calls setContent { }, which invokes RecipeBoxApp(). That composable in turn calls Greeting("RecipeBox"), which calls the built-in Text composable with the string "Hello, RecipeBox!". No XML layout file exists anywhere in this chain — the entire screen is described in Kotlin, top to bottom, in one readable call stack.

Note: This is correct, real Compose/Kotlin code — but this website's own project doesn't run a live Android Studio build, so you won't see it actually render on this page. The next lesson, Composable Functions & Previews, shows how to see composables like Greeting rendered directly inside Android Studio without even running the app.
Where RecipeBox is headed: RecipeBoxApp() will grow into the real root of the app — eventually deciding whether to show the Login screen or the Recipe List, and hosting navigation between Recipe List, Add/Edit Recipe, and Search. For now it just says hello, which is exactly the point: get one composable rendering correctly before adding real screens on top.