New!
Import designs from Figma

The Prototyping tool loved by developers

Everything you need to build high quality responsive app prototypes for every screen size fast. Edit visually, and export to high quality production ready code.

Loved by 70+ developers

Founded by ex-Apple

Over a decade of high quality UX/UI expertise put in a single product.

Build apps, not graphics

Intuitive UI editor, with everything you need to build fully responsive interactive apps for all screen sizes.

Pixel Perfect UI

Continue where you left off with production ready code, as if it was written by a professional coder.
Marcel Teixeira

Marcel Teixeira

A much faster way to build Compose screens and the exported code is pretty much like a human would write, which is amazing! Makes it much easier to pick up where Paper left off and add logic, etc. in Android Studio.
View Tweet
Mane

Mane

Software Dev

I just exported the demo app and liked the resulting code, that was enough for me.

After trying the desktop beta, I'm happy I did it, it's very cool and super easy to use.

View Tweet
TB

Tamer Boykot

Android Dev

It's basically an alternative to Figma with code :)

Examples

Build apps like these

Paper lets you prototype your apps super fast by combining AI speed with full creative control like design tools.

Pixel perfect design with a click of a button

Paper writes production ready clean code instantly. Forget AI slop, forget spaghetti code, only perfectly written code every time

Preview
            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)
                    }
                }
            }
        }
    }
}


Faster than ChatGPT, easier than Code

Learn how to build a responsive Log In screen visually in 60 seconds.

Iterate at the speed of light with Live Preview

Forget waiting for your app to install, cables, 'ADB not responding' and anything that kills your precious productivity. Preview your app in one click and even navigate across different screens.

High quality apps need high quality tooling

Paper got the right thing for the right moment of your building process to bring your ideas to life
Drag & Drop
Intuitive WYSIWYG editor
Live Preview
See changes live without waiting for install
Theme Editor
Create consistent styling across your app
Tablet Overrides
Make your app responsive depending on the screensize
Cross-Platform
Exports to Android, iOS, Desktop & Web
Iconography
Curated icons included straight from the editor
Color Pallete
Hand-picked colors right from the editor
Zero dev handoff
Code ready for devving (for real)

About

Alex Styl
Alex Styl
Solo-founder of BuiltWithPaper.com
Previously at Apple
Presenter at CHI 2017

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.

Powered by
Kotlin
Android Studio compatible
Compose Multiplatform ready

From idea to reality, with one time purchase

Risk free purchase. All offers include 30-day money-back guarantee.
✅ May 2025 offer: 20% off - automatically applied

Solo License

$29

$23

once
Continue to Checkout
Benefits
  • ✅ 1 personal device
  • ✅ Native Mac OS app
  • ✅ One year of updates
  • ✅ Prototype apps for iOS, Android, Desktop & Web
  • ✅ Pixel Perfect Exports
  • ✅ Save apps to JSON
  • ✅ Yours to keep forever

Team License

$64

$51

once
Continue to Checkout
Benefits
  • ✅ 3 personal devices
  • ✅ Native Mac OS app
  • ✅ One year of updates
  • ✅ Prototype apps for iOS, Android, Desktop & Web
  • ✅ Pixel Perfect Exports
  • ✅ Save apps to JSON
  • ✅ Yours to keep forever

Frequently Asked Questions