Prototype apps in minutes
Paper is a prototyping tool that lets you customize anything visually then exports to code, ready to be picked by developers.





Kotlin
Examples
Designed by AI. Edited by a human.
Pixel perfect design with a click of a button

package com.example
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
// ...
@Composable
fun Discover() {
var text by remember { mutableStateOf("") }
val breakpoint = currentScreenWidthBreakpoint()
Column(Modifier.fillMaxSize().background(LocalSurfaceColorColor.current), horizontalAlignment = Alignment.CenterHorizontally) {
CompositionLocalProvider(LocalContentColor provides LocalOnSurfaceColorColor.current) {
TopAppBar(
modifier = Modifier.height(56.dp).fillMaxWidth(),
title = {
Text("Discover", style = LocalTitleLargeTextStyle.current)
},
actions = {
Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
}
},
contentColor = LocalOnSurfaceColorColor.current,
backgroundColor = LocalSurfaceColorColor.current,
contentPadding = PaddingValues(start = 4.dp, top = 0.dp, end = 4.dp, bottom = 0.dp)
)
Column(Modifier.widthIn(max = 600.dp).weight(weight = 1f, fill = false).fillMaxHeight().clip(RoundedCornerShape(LocalCornerRadiusShape.current)).padding(top = 8.dp)) {
Row(Modifier.padding(16.dp)) {
Row(Modifier.height(56.dp).weight(1.0f, fill = false).fillMaxWidth().clip(RoundedCornerShape(LocalCornerRadiusShape.current)).border(1.dp, LocalOutlineColorColor.current, RoundedCornerShape(LocalCornerRadiusShape.current)).background(LocalSurfaceColorColor.current).padding(end = 8.dp, start = 16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp), verticalAlignment = Alignment.CenterVertically) {
CompositionLocalProvider(LocalContentColor provides LocalOnSurfaceColorColor.current) {
Icon(Lucide.Search, contentDescription = "", tint = LocalContentColor.current)
TextField(
value = text,
onValueChange = {
text = it
},
modifier = Modifier.weight(1.0f, fill = false).fillMaxWidth(),
maxLines = 1,
placeholderText = "Search",
contentPadding = PaddingValues(start = 8.dp, top = 12.dp, end = 8.dp, bottom = 12.dp),
borderWidth = 0.dp,
borderColor = LocalOutlineColorColor.current,
fontSize = 16.sp,
singleLine = true
)
Row {
GhostButton(onClick = { /* TODO Handle this */ }, shape = RoundedCornerShape(LocalCornerRadiusShape.current), contentPadding = PaddingValues(8.dp)) {
Icon(Lucide.Mic, contentDescription = "", tint = LocalContentColor.current, modifier = Modifier.alpha(0.66f))
}
}
}
}
}
Row(Modifier.fillMaxWidth().horizontalScroll(rememberScrollState()).padding(end = 16.dp, bottom = 8.dp, start = 16.dp), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
PrimaryButton(onClick = { /* TODO Handle this */ }, shape = RoundedCornerShape(LocalCornerRadiusShape.current), contentColor = LocalOnPrimaryColorColor.current, backgroundColor = LocalPrimaryColorColor.current, borderColor = LocalPrimaryColorColor.current) {
Text("All", fontSize = 16.sp, maxLines = 1)
}
OutlinedButton(onClick = { /* TODO Handle this */ }, shape = RoundedCornerShape(LocalCornerRadiusShape.current), borderColor = LocalOutlineColorColor.current) {
Text("T-Shirts", fontSize = 16.sp, maxLines = 1)
}
OutlinedButton(onClick = { /* TODO Handle this */ }, shape = RoundedCornerShape(LocalCornerRadiusShape.current), borderColor = LocalOutlineColorColor.current) {
Text("Jeans", fontSize = 16.sp, maxLines = 1)
}
OutlinedButton(onClick = { /* TODO Handle this */ }, shape = RoundedCornerShape(LocalCornerRadiusShape.current), borderColor = LocalOutlineColorColor.current) {
Text("Accessories", fontSize = 16.sp, maxLines = 1)
}
OutlinedButton(onClick = { /* TODO Handle this */ }, shape = RoundedCornerShape(LocalCornerRadiusShape.current), borderColor = LocalOutlineColorColor.current) {
Text("Shoes", fontSize = 16.sp, maxLines = 1)
}
OutlinedButton(onClick = { /* TODO Handle this */ }, shape = RoundedCornerShape(LocalCornerRadiusShape.current), borderColor = LocalOutlineColorColor.current) {
Text("Wallets", fontSize = 16.sp, maxLines = 1)
}
}
LazyVerticalGrid(if (breakpoint isAtLeast ScreenWidthBreakpoint.Small) GridCells.Fixed(3) else GridCells.Fixed(2), modifier = Modifier.fillMaxWidth().weight(weight = 1f, fill = false).fillMaxHeight(), contentPadding = PaddingValues(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 8.dp), verticalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
item {
Column(Modifier.fillMaxWidth()) {
Image(rememberAsyncImagePainter("https://images.unsplash.com/photo-1515886657613-9f3515b0c78f?q=80&w=2124&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"), contentDescription = "", modifier = Modifier.height(300.dp).fillMaxWidth().clip(RoundedCornerShape(LocalCornerRadiusShape.current)), contentScale = ContentScale.Crop)
Column(Modifier.fillMaxWidth().padding(top = 16.dp, bottom = 16.dp), verticalArrangement = Arrangement.spacedBy(4.dp)) {
Text("Yellow Outfit", modifier = Modifier.fillMaxWidth(), fontSize = 20.sp, fontWeight = FontWeight(500))
Text("$120.00", modifier = Modifier.fillMaxWidth().alpha(0.6f), fontSize = 16.sp)
}
}
}
item {
Column(Modifier.fillMaxWidth()) {
Image(rememberAsyncImagePainter("https://images.unsplash.com/photo-1487222477894-8943e31ef7b2?q=80&w=3408&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"), contentDescription = "", modifier = Modifier.height(300.dp).fillMaxWidth().clip(RoundedCornerShape(LocalCornerRadiusShape.current)), contentScale = ContentScale.Crop)
Column(Modifier.fillMaxWidth().padding(top = 16.dp, bottom = 16.dp), verticalArrangement = Arrangement.spacedBy(4.dp)) {
Text("Leather Jacket", modifier = Modifier.fillMaxWidth(), fontSize = 20.sp, fontWeight = FontWeight(500))
Text("$190.00", modifier = Modifier.fillMaxWidth().alpha(0.6f), fontSize = 16.sp)
}
}
}
item {
Column(Modifier.fillMaxWidth()) {
Image(rememberAsyncImagePainter("https://images.unsplash.com/photo-1554838692-3b50e4261b6f?q=80&w=3456&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"), contentDescription = "Red Shades photo", modifier = Modifier.height(300.dp).fillMaxWidth().clip(RoundedCornerShape(LocalCornerRadiusShape.current)), contentScale = ContentScale.Crop)
Column(Modifier.fillMaxWidth().padding(top = 16.dp, bottom = 16.dp), verticalArrangement = Arrangement.spacedBy(4.dp)) {
Text("Red Shades", modifier = Modifier.fillMaxWidth(), fontSize = 20.sp, fontWeight = FontWeight(500))
Text("$50.00", modifier = Modifier.fillMaxWidth().alpha(0.6f), fontSize = 16.sp)
}
}
}
item {
Column(Modifier.fillMaxWidth()) {
Image(rememberAsyncImagePainter("https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?q=80&w=3000&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"), contentDescription = "", modifier = Modifier.height(300.dp).fillMaxWidth().clip(RoundedCornerShape(LocalCornerRadiusShape.current)), contentScale = ContentScale.Crop)
Column(Modifier.fillMaxWidth().padding(top = 16.dp, bottom = 16.dp), verticalArrangement = Arrangement.spacedBy(4.dp)) {
Text("Basic T-Shirt", modifier = Modifier.fillMaxWidth(), fontSize = 20.sp, fontWeight = FontWeight(500))
Text("$20.00", modifier = Modifier.fillMaxWidth().alpha(0.6f), fontSize = 16.sp)
}
}
}
item {
Column(Modifier.fillMaxWidth()) {
Image(rememberAsyncImagePainter("https://images.unsplash.com/photo-1510060637021-6287bd1b5232?q=80&w=3542&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"), contentDescription = "Basic Cap photo", modifier = Modifier.height(300.dp).fillMaxWidth().clip(RoundedCornerShape(LocalCornerRadiusShape.current)), contentScale = ContentScale.Crop)
Column(Modifier.fillMaxWidth().padding(top = 16.dp, bottom = 16.dp), verticalArrangement = Arrangement.spacedBy(4.dp)) {
Text("Basic Cap", modifier = Modifier.fillMaxWidth(), fontSize = 20.sp, fontWeight = FontWeight(500))
Text("$35.00", modifier = Modifier.fillMaxWidth().alpha(0.6f), fontSize = 16.sp)
}
}
}
item {
Column(Modifier.fillMaxWidth()) {
Image(rememberAsyncImagePainter("https://images.unsplash.com/photo-1519027356316-9f99e11d8bac?q=80&w=3870&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"), contentDescription = "", modifier = Modifier.height(300.dp).fillMaxWidth().clip(RoundedCornerShape(LocalCornerRadiusShape.current)), contentScale = ContentScale.Crop)
Column(Modifier.fillMaxWidth().padding(top = 16.dp, bottom = 16.dp), verticalArrangement = Arrangement.spacedBy(4.dp)) {
Text("Cardigan", modifier = Modifier.fillMaxWidth(), fontSize = 20.sp, fontWeight = FontWeight(500))
Text("$90.00", modifier = Modifier.fillMaxWidth().alpha(0.6f), fontSize = 16.sp)
}
}
}
}
}
BottomNavigation(modifier = Modifier.heightIn(56.dp).fillMaxWidth().shadow(4.dp).zIndex(4.0f).background(if (breakpoint isAtLeast ScreenWidthBreakpoint.Small) LocalSurfaceColorColor.current else Color.Unspecified), contentColor = LocalOnSurfaceColorColor.current, backgroundColor = LocalSurfaceColorColor.current) {
TabItem(selected = true, onSelected = { /* TODO Handle this */ }, modifier = Modifier.weight(1.0f, fill = false).fillMaxWidth(), selectedColor = LocalPrimaryColorColor.current, contentColor = LocalContentColor.current) {
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(space = 4.dp, alignment = Alignment.CenterVertically)) {
Icon(Lucide.House, contentDescription = "", tint = LocalContentColor.current)
Text("Discover", fontSize = 16.sp)
}
}
TabItem(selected = false, onSelected = { /* TODO Handle this */ }, modifier = Modifier.weight(1.0f, fill = false).fillMaxWidth(), selectedColor = LocalPrimaryColorColor.current, contentColor = LocalContentColor.current) {
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(space = 4.dp, alignment = Alignment.CenterVertically)) {
Icon(Lucide.ShoppingCart, contentDescription = "", tint = LocalContentColor.current)
Text("Cart", fontSize = 16.sp)
}
}
TabItem(selected = false, onSelected = { /* TODO Handle this */ }, modifier = Modifier.weight(1.0f, fill = false).fillMaxWidth(), selectedColor = LocalPrimaryColorColor.current, contentColor = LocalContentColor.current) {
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(space = 4.dp, alignment = Alignment.CenterVertically)) {
Icon(Lucide.ContactRound, contentDescription = "", tint = LocalContentColor.current)
Text("Account", fontSize = 16.sp)
}
}
}
}
}
}
High quality apps need high quality tooling
Faster than ChatGPT, easier than Code
About
I've spent 10+ building native apps in companies such as Apple, product studios and my own startups.
99% of the time, I want to focus on the app instead of the code. I'm fast with code, but trying out the changes on the device takes forever. You need to build, install, see the app on the device etc.
So I built Paper.
It works like a professional design tool and exports clean, professional code. You move fast without touching code, but still get full control when needed.
Paper is a bootstrapped company. There is no VC funding, all profits go directly to improving the product for you (and paying my rent and coffee). This means that every single decision made is to improve the product for you, while running a sustainable business that will last for years to come.