A Hello World in Jetpack Compose is more than just printing text — it’s your first encounter with composables, themes, and the Compose UI tree. This guide walks through building a complete minimal app and explains every part.
The fastest way: New Project
In Android Studio: File → New → New Project → Empty Compose Activity. This generates a working Compose project. But let’s understand what it creates and how to build one from scratch.
Project structure
A minimal Compose project looks like this:
HelloCompose/
├── app/
│ ├── build.gradle.kts # App-level Gradle config + Compose deps
│ └── src/main/
│ ├── AndroidManifest.xml # App declaration
│ ├── java/com/example/hello/
│ │ ├── MainActivity.kt # Entry point — sets content
│ │ └── ui/theme/ # Theme, colors, typography
│ │ ├── Theme.kt
│ │ ├── Color.kt
│ │ └── Type.kt
│ └── res/
│ └── values/
│ └── strings.xml
├── build.gradle.kts # Project-level Gradle config
├── settings.gradle.kts
└── gradle.properties
Step 1: Project-level build.gradle.kts
Configure Kotlin and the Compose compiler plugin:
plugins {
id("com.android.application") version "8.5.0" apply false
id("org.jetbrains.kotlin.android") version "2.0.0" apply false
id("org.jetbrains.kotlin.plugin.compose") version "2.0.0" apply false
}Step 2: App-level build.gradle.kts
Add the Compose dependencies using the BOM:
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.plugin.compose")
}
android {
namespace = "com.example.hello"
compileSdk = 35
defaultConfig {
applicationId = "com.example.hello"
minSdk = 24
targetSdk = 35
versionCode = 1
versionName = "1.0"
}
buildFeatures {
compose = true
}
kotlin {
jvmToolchain(17)
}
}
dependencies {
val composeBom = platform("androidx.compose:compose-bom:2024.06.00")
implementation(composeBom)
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.foundation:foundation")
implementation("androidx.activity:activity-compose:1.9.0")
debugImplementation("androidx.compose.ui:ui-tooling")
}Step 3: AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.Material3.DynamicColors.DayNight">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>Step 4: The MainActivity
This is the entry point. setContent bridges the Android Activity world into Compose:
package com.example.hello
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "World",
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello, $name!",
modifier = modifier
)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
MaterialTheme {
Greeting(name = "World")
}
}What’s happening here
Let’s break down each piece:
ComponentActivity and setContent
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// This lambda is your Compose UI tree
}
}
}| Piece | What it does |
|---|---|
ComponentActivity |
Base activity that supports Compose |
onCreate |
Called when the activity starts |
setContent { } |
Replaces setContentView(R.layout.xml) — defines your UI as composables |
@Composable functions
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello, $name!",
modifier = modifier
)
}| Piece | What it does |
|---|---|
@Composable |
Marks this as a Compose UI function (like a reusable component) |
name parameter |
Data passed into the composable (like a prop) |
modifier parameter |
Customizes layout, padding, click handling — always include this |
Text() |
A built-in composable that renders text on screen |
Modifier
Modifiers are how you configure composables — padding, size, click handlers, borders, etc:
Text(
text = "Hello, World!",
modifier = Modifier
.padding(16.dp)
.fillMaxWidth()
)Order matters. Modifier.padding(16.dp).fillMaxWidth() pads inside the width, while Modifier.fillMaxWidth().padding(16.dp) pads outside.
MaterialTheme
Wraps your UI in Material Design theming (colors, typography, shapes):
MaterialTheme {
Greeting(name = "World")
}Without it, Text still works but uses default system styling. With it, Text picks up your app’s Material theme.
Scaffold
Provides the basic Material layout structure — top bar, bottom bar, floating action button, and a content area:
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "World",
modifier = Modifier.padding(innerPadding)
)
}innerPadding ensures your content doesn’t overlap the status bar or navigation bar.
@Preview
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
MaterialTheme {
Greeting(name = "World")
}
}Previews render in Android Studio’s design view without running the app. They must be:
- Top-level functions (not nested)
- Parameterless (or use
@PreviewParameter) - Wrapped in a theme for realistic rendering
A more interactive Hello World
Let’s add a button and some state to make it interactive:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
HelloWorld(modifier = Modifier.padding(innerPadding))
}
}
}
}
}
@Composable
fun HelloWorld(modifier: Modifier = Modifier) {
var name by remember { mutableStateOf("") }
Column(
modifier = modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text(
text = if (name.isBlank()) "Hello, World!" else "Hello, $name!",
style = MaterialTheme.typography.headlineMedium
)
OutlinedTextField(
value = name,
onValueChange = { name = it },
label = { Text("Enter your name") },
singleLine = true
)
}
}What’s new here
| Concept | Explanation |
|---|---|
remember |
Saves state across recompositions (but not configuration changes) |
mutableStateOf |
Creates observable state — when name changes, Compose re-renders |
by delegation |
Lets you read/write name as a plain String instead of name.value |
Column |
Lays out children vertically with optional spacing |
OutlinedTextField |
Material Design text input |
Running the app
# Build and install on a connected device or emulator
./gradlew installDebug
# Or just build
./gradlew assembleDebugIn Android Studio, click Run (green play button) or press Shift+F10.
Common problems
| Problem | Solution |
|---|---|
| “Compose not found” | Make sure buildFeatures { compose = true } is in your app-level Gradle |
| “Unresolved reference: setContent” | Add implementation("androidx.activity:activity-compose:1.9.0") |
| “Unresolved reference: MaterialTheme” | Add implementation("androidx.compose.material3:material3") and use the Compose BOM |
| App crashes on launch | Check AndroidManifest.xml — your activity must be declared with MAIN/LAUNCHER intent filter |
| Preview not showing | Make sure the @Preview function is top-level, not inside a class |
| “by remember” syntax error | Import androidx.compose.runtime.getValue and setValue (Kotlin property delegation) |
Complete minimal file list
If you want the absolute smallest working Compose app, here are the only files that matter:
settings.gradle.kts
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
rootProject.name = "HelloCompose"
include(":app")app/build.gradle.kts — as shown in Step 2
app/src/main/AndroidManifest.xml — as shown in Step 3
app/src/main/java/com/example/hello/MainActivity.kt — as shown in Step 4
That’s it. Four files and you have a running Compose app.
Next steps
Now that you have Hello World working:
- State management → How to manage state in Compose
- Material Design → Material Design 3 in Compose
- Navigation → Navigate between screens
- Testing → Write Compose tests