
Compose Unstyled 2.0 is finally out, and I'm incredibly stoked I get to talk to you about it!
The goal for Compose Unstyled moving forward is to become the API you use to build your own Compose component libraries.
Every design choice from now on is about giving component library authors the right tools to build any components they need.
Compose Unstyled components are not meant to be used throughout your codebase. Instead, wrap them in your own components to save you hours or days of work.
Here is what is included, and how this release gets us there:
- New composable component API design – components are now split into small, composable pieces you can arrange however your design system needs.
- New Dropdown and Tooltip APIs – trigger and floating panel APIs now align around anchored placement.
- Modules! – all components and APIs now live in their own modules so you can mix and match.
- Unstyled to the core – all opinionated styling options are now gone.
- All deprecated APIs are now gone – new major release, new fresh start.
New composable component API design
Components in Compose Unstyled now share a unified API design, making them easier to compose together.
From now on, all components are prefixed with Unstyled. This makes it easier to differentiate when a component is part of your design system (i.e. Button vs UnstyledButton).
Components prefixed with Unstyled are the entry point for building components in your design system.
Most component APIs now expose multiple composables that can only be used within the scope of their respective Unstyled primitive.
Take a look at the UnstyledBottomSheet:
val sheetState = rememberBottomSheetState(
initialDetent = SheetDetent.Hidden,
)
UnstyledBottomSheet(state = sheetState) {
Sheet {
DragIndication()
}
}
Notice how the bottom sheet consists of three different pieces, not just one:
- The
UnstyledBottomSheetrepresents the area in which the sheet can be dragged. - The
Sheetcomponent represents the visible part of the sheet that the user can drag and move. - The
DragIndicationtoggles the sheet visibility when clicked, for accessibility purposes.
This level of breakdown gives design system authors exactly the amount of granularity they need for their components.
New Dropdown and Tooltip APIs
Dropdowns and tooltips have a lot in common. They both start from an anchor, and they both render floating content next to it.
Because of that, their APIs have been reworked around the same model: an anchor slot, a panel slot, and placement parameters for controlling where the panel appears.
var expanded by remember { mutableStateOf(false) }
UnstyledDropdownMenu(
expanded = expanded,
onExpandedChange = { expanded = it },
side = AnchorSide.Bottom,
alignment = AnchorAlignment.End,
sideOffset = 8.dp,
alignmentOffset = 0.dp,
panel = {
DropdownMenuPanel {
MenuItem(onClick = onEdit) {
Text("Edit")
}
MenuItem(onClick = onDelete) {
Text("Delete")
}
}
},
anchor = {
UnstyledButton(onClick = { expanded = true }) {
Text("Open")
}
},
)
This makes it easier to build menus and tooltips that feel like they belong to the same component library. Both APIs can be positioned using side, alignment, sideOffset, and alignmentOffset, while you still own the exact visuals yourself.
Modules!
One big blocker people had when using Compose Unstyled was the size of its API surface. The library grew a lot over time, to the point where it brought in many unrelated pieces.
Some people only want to use specific Unstyled components because they offer greater customizability and, in many cases, more stability than Jetpack Compose's default ones.
Other folks who want to build a design system do not want to pull in every single component in the library at once.
Because of this, Compose Unstyled is now split into modules. Every API now lives in its own tiny module that you can add to your app only when you need it.
Here is what this looks like:
implementation("com.composables:composeunstyled-theming:2.0.0")
implementation("com.composables:composeunstyled-button:2.0.0")
implementation("com.composables:composeunstyled-checkbox:2.0.0")
implementation("com.composables:composeunstyled-dialog:2.0.0")
implementation("com.composables:composeunstyled-dropdown-menu:2.0.0")
implementation("com.composables:composeunstyled-modal:2.0.0")
implementation("com.composables:composeunstyled-modal-bottom-sheet:2.0.0")
implementation("com.composables:composeunstyled-progress:2.0.0")
implementation("com.composables:composeunstyled-radio-group:2.0.0")
implementation("com.composables:composeunstyled-slider:2.0.0")
implementation("com.composables:composeunstyled-tab-group:2.0.0")
implementation("com.composables:composeunstyled-text-field:2.0.0")
implementation("com.composables:composeunstyled-toggle-switch:2.0.0")
implementation("com.composables:composeunstyled-tooltip:2.0.0")
and many more. Check out the documentation for a full list.
Unstyled to the core
One of the promises of Compose Unstyled is that it makes no design choices for you. There should be no limits on the design system you want to build.
Even though 1.x did a great job enabling custom components, it still provided a few opinionated styling options.
Most components came with options to style their background, shape, and contentColor. Some components also came with layout choices baked in, such as wrapping their content in a Column. Even though the intent was good, the approach was not right. contentColor, for example, was implemented by introducing a new LocalContentColor local, which got in the way of using the components together with existing design systems.
Because of this, styling parameters and default layouts have been removed from Compose Unstyled. Instead, the author of the design system is responsible for styling and arranging their components as they see fit using the respective Modifier.
All deprecated APIs are now gone
Every deprecated API has now been removed from the codebase. No functionality has been removed, but there were a few small breaking changes.
Some APIs were simplified too. For example, dialogs no longer need a state object and can now be controlled with a simple boolean.
On the bright side, the cost of migrating is minimal thanks to the modularization of the project.
Upgrade to Compose Unstyled 2.0
That's all for this release folks.
Go check the brand-new documentation for lots of code examples showing the new APIs, plus a migration guide for moving to 2.0.
Can't wait to see all the new components you build with this one. Send me a message over the Discord.
Talk soon, – Alex