1# API Guidelines for `@Composable` components in Jetpack Compose 2 3## Last updated: July 19, 2023 4 5Set of guidelines and recommendations for building scalable and user-friendly @Composable components. 6 7The requirement level of each of these guidelines is specified using the terms set forth in [RFC2119](https://www.ietf.org/rfc/rfc2119.txt) for each of the following developer audiences. If an audience is not specifically named with a requirement level for a guideline, it should be assumed that the guideline is OPTIONAL for that audience. 8 9### Jetpack Compose framework development 10 11Contributions to the androidx.compose libraries and tools generally follow these guidelines to a strict degree to promote consistency, setting expectations and examples for consumer code at all layers. 12 13### Library development based on Jetpack Compose 14 15It is expected and desired that an ecosystem of external libraries will come to exist that target Jetpack Compose, exposing a public API of `@Composable` functions and supporting types for consumption by apps and other libraries. While it is desirable for these libraries to follow these guidelines to the same degree as Jetpack Compose framework development would, organizational priorities and local consistency may make it appropriate for some purely stylistic guidelines to be relaxed. 16 17### App development based on Jetpack Compose 18 19App development is often subject to strong organizational priorities and norms and requirements to integrate with existing app architecture. This may call for not only stylistic deviation from these guidelines but structural deviation as well. Where possible, alternative approaches for app development will be listed in this document that may be more appropriate in these situations. 20 21## Table of content 22- [Note on vocabulary in this doc](#note-on-vocabulary-in-this-doc) 23- [Before you create a component](#before-you-create-a-component) 24 - [Component’s purpose](#components-purpose) 25 - [Component layering](#component-layering) 26 - [Do you need a component?](#do-you-need-a-component) 27 - [Component or Modifier](#component-or-modifier) 28- [Name of a Component](#name-of-a-component) 29 - [BasicComponent vs Component](#basiccomponent-vs-component) 30 - [Design, Usecase or Company/Project specific prefixes](#design-usecase-or-companyproject-specific-prefixes) 31- [Component dependencies](#component-dependencies) 32 - [Prefer multiple components over style classes](#prefer-multiple-components-over-style-classes) 33 - [Explicit vs implicit dependencies](#explicit-vs-implicit-dependencies) 34- [Component parameters](#component-parameters) 35 - [Parameters vs. Modifier on the component](#parameters-vs-modifier-on-the-component) 36 - [`modifier` parameter](#modifier-parameter) 37 - [Parameters order](#parameters-order) 38 - [Nullable parameter](#nullable-parameter) 39 - [Default expressions](#default-expressions) 40 - [MutableState\<T\> as a parameter](#mutablestatet-as-a-parameter) 41 - [State\<T\> as a parameter](#statet-as-a-parameter) 42 - [Slot parameters](#slot-parameters) 43 - [What are slots](#what-are-slots) 44 - [Why slots](#why-slots) 45 - [Single “content” slot overloads](#single-content-slot-overloads) 46 - [Layout strategy scope for slot APIs](#layout-strategy-scope-for-slot-apis) 47 - [Lifecycle expectations for slot parameters](#lifecycle-expectations-for-slot-parameters) 48 - [DSL based slots](#dsl-based-slots) 49- [Component-related classes and functions](#component-related-classes-and-functions) 50 - [State](#state) 51 - [ComponentDefault object](#componentdefault-object) 52 - [ComponentColor/ComponentElevation objects](#componentcolorcomponentelevation-objects) 53- [Documentation for the component](#documentation-for-the-component) 54 - [Documentation structure and ordering](#documentation-structure-and-ordering) 55 - [Documentation example](#documentation-example) 56- [Accessibility of the component](#accessibility-of-the-component) 57 - [Semantics merging](#semantics-merging) 58 - [Accessibility related parameters](#accessibility-related-parameters) 59 - [Accessibility tuning](#accessibility-tuning) 60- [Tooling support](#tooling-support) 61- [Evolution of the Component APIs](#evolution-of-the-component-apis) 62 63## Note on vocabulary in this doc 64 65**@Composable component** - A @Composable function that returns `Unit` and emits the UI when it is composed in a hierarchy (later: component). 66 67**Developer** - a person who creates a component that is to be used by a user in an application or in another component library. 68 69**User** - the user of the component - a person who uses the component in a composable hierarchy to show some ui to the end-user. 70 71**End-user** - the person who will use the application created by the user of your component. 72 73These guidelines outline the best practices for developing UI components using Jetpack Compose. Best practices ensure that the API of the components is: 74 75* **Scalable long term**: the author is able to evolve the API to cause the least amount of friction to users. 76* **Consistent across other components**: developers can use existing knowledge and patterns to work with new components that are created by different authors. 77* **Guide developers towards the happy path**: components will encourage the right practices and usages and disallow the incorrect usage where possible. 78 79## Before you create a component 80 81When creating a new component: 82 83* Make sure there’s a single problem the component solves. Split components into subcomponents and building blocks until each solves a single problem users have. 84* Make sure you need a component and it brings value that justifies the long term support and evolution of its APIs. Often developers might find that it is easier for users to write the component code themselves so they can adjust later. 85 86### Component’s purpose 87 88Consider the value a new component adds and the problem it solves. Each component should solve only **one** problem, and each problem should be solved in **one** place. If your component solves more than one problem look for opportunities to split it into layers or subcomponents. With the benefit of smaller, concise and use case targeted API comes the easy of use and clear understanding of the component contract. 89 90Lower level building blocks and components usually add certain new single functionality and are easy to combine together. Higher level components serve a purpose of combining building blocks to provide an opinionated, ready to use behavior. 91 92**DON’T** 93``` 94// avoid multipurpose components: for example, this button solves more than 1 problem 95@Composable 96fun Button( 97 // problem 1: button is a clickable rectangle 98 onClick: () -> Unit = {}, 99 // problem 2: button is a check/uncheck checkbox-like component 100 checked: Boolean = false, 101 onCheckedChange: (Boolean) -> Unit, 102) { ... } 103``` 104 105**Do:** 106``` 107@Composable 108fun Button( 109 // problem 1: button is a clickable rectangle 110 onClick: () -> Unit, 111) { ... } 112 113@Composable 114fun ToggleButton( 115 // problem 1: button is a check/uncheck checkbox-like component 116 checked: Boolean, 117 onCheckedChange: (Boolean) -> Unit, 118) { ... } 119``` 120 121### Component layering 122 123When creating components, provide various layers of single purpose building blocks first that are needed for the component to work. Increase level of opinion ingrained and reduce the amount of customisations as you go from low level APIs to higher level. Higher level components should provide more opinionated defaults and fewer customisation options. 124 125`@Composable` component creation was designed to be a low-effort operation in Compose so that users can create their own single purpose components and adjust them as needed. 126 127**Do:** 128``` 129// single purpose building blocks component 130@Composable 131fun Checkbox(...) { ... } 132 133@Composable 134fun Text(...) { ... } 135 136@Composable 137fun Row(...) { ... } 138 139// high level component that is more opinionated combination of lower level blocks 140@Composable 141fun CheckboxRow(...) { 142 Row { 143 Checkbox(...) 144 Spacer(...) 145 Text(...) 146 } 147} 148``` 149 150### Do you need a component? 151 152Question the need for creating the component in the first place. With high-level components that can be combined from building blocks, there has to be a strong reason for it to exist. Lower level components should solve a real problem that users have. 153 154Try to create a Component from the publicly available building blocks. This provides the sense of what it feels like to be a developer who needs your component. If it looks simple, readable and doesn’t require hidden knowledge to make - this means users can do it themselves. 155 156Consider the value your component brings to users if they choose it over doing it themselves. Consider the burden a component puts on a user who would need to learn new APIs to use them. 157 158For example, a developer wants to create a `RadioGroup` component. In order to accommodate various requirements such as vertical and horizontal layouts, different types of data and decorations, the API might look like this: 159``` 160@Composable 161fun <T> RadioGroup( 162 // `options` are a generic type 163 options: List<T>, 164 // horizontal or vertical 165 orientation: Orientation, 166 // some adjustments around content layout 167 contentPadding: PaddingValues, 168 modifier: Modifier = Modifier, 169 optionContent: @Composable (T) -> Unit 170) { ... } 171``` 172 173While doing this, look first at how users would write it themselves using the available building blocks: 174``` 175// Modifier.selectableGroup adds semantics of a radio-group like behavior 176// accessibility services will treat it as a parent of various options 177Column(Modifier.selectableGroup()) { 178 options.forEach { item -> 179 Row( 180 modifier = Modifier.selectable( 181 selected = (select.value == item), 182 onClick = { select.value = item } 183 ), 184 verticalAlignment = Alignment.CenterVertically 185 ) { 186 Text(item.toString()) 187 RadioButton( 188 selected = (select.value == item), 189 onClick = { select.value = item } 190 ) 191 } 192 } 193} 194``` 195 196Now, developers should make a conscious decision on whether the `RadioGroup` API is worth it. In this particular example, users utilize familiar building blocks such as `Row`, `Text` and other basic tools. They also gain the flexibility to define the layouts needed or add any decorations and customisations. The case might be made to not to introduce any `RadioGroup` APIs at all. 197 198Shipping a component is costly, involving at least the development, testing, long term support and subsequent evolution of the API. 199 200### Component or Modifier 201 202Make a component if it has a distinct UI that cannot be applied to other components or if the component wants to make structural changes in the UI (add/remove other components). 203 204Make the feature to be a Modifier instead if the bit of functionality can be applied to any arbitrary **single** component to add extra behavior. This is especially important when the functionality has undefined behavior when applied to a few UI components at the same time. 205 206**DON’T** 207``` 208@Composable 209fun Padding(allSides: Dp) { 210 // impl 211} 212 213// usage 214Padding(12.dp) { 215 // 1. Is it a padding around both card and picture or for each one? 216 // 2. What are the layout expectations for card and picture? 217 // 3. What if there is no content (no card and picture at all)? 218 UserCard() 219 UserPicture() 220} 221``` 222 223**Do:** 224``` 225fun Modifier.padding(allSides: Dp): Modifier = // implementation 226 227// usage 228UserCard(modifier = Modifier.padding(12.dp)) 229``` 230 231If the bit of functionality can be applied to any composable, but it has to alter the hierarchy of composables, it has to be a Component, since Modifiers cannot change the hierarchy: 232 233**Do** 234``` 235@Composable 236fun AnimatedVisibility( 237 visibile: Boolean, 238 modifier: Modifier = Modifier, 239 content: @Composable () -> Unit 240) { 241 // impl 242} 243 244// usage: AnimatedVisibility has to have power to remove/add UserCard 245// to hierarchy depending on the visibility flag 246AnimatedVisibility(visible = false) { 247 UserCard() 248} 249``` 250 251## Name of a Component 252 253Please, refer to the corresponding [Compose API guidelines](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-api-guidelines.md#naming-unit-composable-functions-as-entities) section for naming conventions. However, there are more detailed considerations to keep in mind. 254 255**Jetpack Compose framework development** MUST follow the rules in this section. 256 257**Library development** MUST follow the section below. 258 259**App development** MAY follow the rules below. 260 261### BasicComponent vs Component 262 263Consider `Basic*` prefix for components that provide barebones functionality with no decoration and/or with no design-system based visual opinions. This is a signal that users are expected to wrap it in their own decoration, as the component is not expected to be used as-is. As a counterpart to that, `Component` name without a prefix can represent components that are ready to use and are decorated according to some design specification. 264 265**Do:** 266``` 267// component that has no decoration, but basic functionality 268@Composable 269fun BasicTextField( 270 value: TextFieldValue, 271 onValueChange: (TextFieldValue) -> Unit, 272 modifier: Modifier = Modifier, 273 ... 274) 275 276// ready to use component with decorations 277@Composable 278fun TextField( 279 value: TextFieldValue, 280 onValueChange: (TextFieldValue) -> Unit, 281 modifier: Modifier = Modifier, 282 ... 283) 284``` 285 286### Design, Usecase or Company/Project specific prefixes 287 288Avoid `CompanyName` (`GoogleButton`) or Module (`WearButton`) prefixes where possible and consider use-case or domain specific names if needed to. If the component you are building is a part of component library built using ``compose-foundation`` or ``compose-ui`` building blocks as a basis, the majority of the non-prefixed names should be available to developers without clashes: `com.companyname.ui.Button` or `com.companyname.ui.Icon`. Simple names make sure these components feel first-class when used. 289 290If wrapping existing components or building on top of another design system, consider names that are derived from the use case first: `ScalingLazyColumn`, `CurvedText`. If impossible or the use case clashes with the existing component, module/library prefix can be used e.g. `GlideImage.` 291 292If your design system specification introduces a number of similar components with different appearances, consider using specification prefixes: `ContainedButton`, `OutlinedButton`, `SuggestionChip`, etc. Using prefixes helps you avoid “style” patterns and keep the API simple. See "[ComponentColor/ComponentElevation](#componentcolorcomponentelevation-objects)" section for more details. 293 294If you have a set of components with prefixes, consider choosing the default component, which is the one most likely to be used, and keep it without the prefix. 295 296**Do** 297``` 298// This button is called ContainedButton in the spec 299// It has no prefix because it is the most common one 300@Composable 301fun Button(...) {} 302 303// Other variations of buttons below: 304@Composable 305fun OutlinedButton(...) {} 306 307@Composable 308fun TextButton(...) {} 309 310@Composable 311fun GlideImage(...) {} 312``` 313 314**Also do (if your library is based on compose-foundation)** 315``` 316// package com.company.project 317// depends on foundation, DOES NOT depend on material or material3 318 319@Composable 320fun Button(...) {} // simple name that feel like a first-class button 321 322@Composable 323fun TextField(...) {} // simple name that feel like a first-class TF 324 325``` 326 327## Component dependencies 328 329**Jetpack Compose framework development** MUST follow the rules in this section. 330 331**Library development** SHOULD follow the section below. 332 333**App development** MAY follow the rules below. 334 335### Prefer multiple components over style classes 336 337Express dependencies in a granular, semantically meaningful way. Avoid grab-bag style parameters and classes, akin to `ComponentStyle` or `ComponentConfiguration`. 338 339When a certain subset of components of the same type need to have the same configurations or stylistical visual appearance, users should be encouraged to create their own semantically meaningful version of a component. This can be done either by wrapping the component or forking it and using lower-level building blocks. This is the component developer’s responsibility to make sure that both of those ways are low cost operations. 340 341Instead of relying on the `ComponentStyle` to specify different component variations in the component library, consider providing separate `@Composable` functions named differently to signify the difference in styling and use cases for those components. 342 343**DON’T** 344 345``` 346// library code 347class ButtonStyles( 348 /* grab bag of different parameters like colors, paddings, borders */ 349 background: Color, 350 border: BorderStroke, 351 textColor: Color, 352 shape: Shape, 353 contentPadding: PaddingValues 354) 355 356val PrimaryButtonStyle = ButtonStyle(...) 357val SecondaryButtonStyle = ButtonStyle(...) 358val AdditionalButtonStyle = ButtonStyle(...) 359 360@Composable 361fun Button( 362 onClick: () -> Unit, 363 style: ButtonStyle = SecondaryButtonStyle 364) { 365 // impl 366} 367 368// usage 369val myLoginStyle = ButtonStyle(...) 370Button(style = myLoginStyle) 371``` 372 373**Do:** 374``` 375// library code 376@Composable 377fun PrimaryButton( 378 onClick: () -> Unit, 379 background: Color, 380 border: BorderStroke, 381 // other relevant parameters 382) { 383 // impl 384} 385 386@Composable 387fun SecondaryButton( 388 onClick: () -> Unit, 389 background: Color, 390 border: BorderStroke, 391 // other relevant parameters 392) { 393 // impl 394} 395 396// usage 1: 397PrimaryButton(onClick = { loginViewModel.login() }, border = NoBorder) 398// usage 2: 399@Composable 400fun MyLoginButton( 401 onClick: () -> Unit 402) { 403 // delegate to and wrap other components or its building blocks 404 SecondaryButton( 405 onClick, 406 background = MyLoginGreen, 407 border = LoginStroke 408 ) 409} 410``` 411 412### Explicit vs implicit dependencies 413 414Prefer explicit inputs and configuration options in your components, such as function parameters. Explicit inputs for the component make it easy to predict the component's behavior, adjust it, test and use. 415 416Avoid implicit inputs provided via `CompositionLocal` or other similar mechanisms. Those inputs add complexity to the components and every usage of it and make it hard to track where customisation comes from for users. To avoid implicit dependencies, make it easy for users to create their own opinionated components with a subset of explicit inputs they wish to customize. 417 418**DON’T** 419``` 420// avoid composition locals for component specific customisations 421// they are implicit. Components become difficult to change, test, use. 422val LocalButtonBorder = compositionLocalOf<BorderStroke>(...) 423 424@Composable 425fun Button( 426 onClick: () -> Unit, 427) { 428 val border = LocalButtonBorder.current 429} 430 431``` 432 433**Do:** 434``` 435@Composable 436fun Button( 437 onClick: () -> Unit, 438 // explicitly asking for explicit parameter that might have 439 // reasonable default value 440 border: BorderStroke = ButtonDefaults.borderStroke, 441) { 442 // impl 443} 444``` 445 446Consider using `CompositionLocal` to provide a global app or screen styling if needed. For example, design theming or typography in the material library can be implicitly specified for the whole app or screen. When doing so, make sure that those CompositionLocals are being read in the default expressions on the component parameters, so users can override them. 447 448Since those objects rarely change and cover big subtrees of components of different kinds, the flexibility of app-wide customisation is usually worth the aforementioned downsides of the implicit inputs. In cases like this, components should be discouraged to read this `CompositionLocal` in implementation and instead read it in the default expressions, so it is easy to override when customizing or wrapping the component. 449 450**DON’T** 451``` 452// this is ok: theme is app global, but... 453class Theme(val mainAppColor: Color) 454val LocalAppTheme = compositionLocalOf { Theme(Color.Green) } 455 456@Composable 457fun Button( 458 onClick: () -> Unit, 459) { 460 // reading theme in implementation makes it impossible to opt out 461 val buttonColor = LocalAppTheme.current.mainAppColor 462 Box(modifier = Modifier.background(buttonColor)) { ... } 463} 464 465``` 466 467**Do:** 468``` 469// this is ok: theme is app global 470class Theme(val mainAppColor: Color) 471val LocalAppTheme = compositionLocalOf { Theme(Color.Green) } 472 473@Composable 474fun Button( 475 onClick: () -> Unit, 476 // easy to see where the values comes from and change it 477 backgroundColor: Color = LocalAppTheme.current.mainAppColor 478) { 479 Box(modifier = Modifier.background(backgroundColor)) { ... } 480} 481``` 482 483_There’s a [blogpost](https://medium.com/androiddevelopers/pushing-the-right-buttons-in-jetpack-compose-124cb4b17197) published that describes the reasoning in depth in the chapter “Maintaining API consistency”._ 484 485## Component parameters 486 487Set of considerations regarding parameters of `@Composable` component. 488 489**Jetpack Compose framework development** MUST follow the rules in this section below. 490 491**Compose library development** SHOULD follow the rules in the sections below. 492 493**App development** SHOULD follow. 494 495### Parameters vs. Modifier on the component 496 497Do not introduce optional parameters that add optional behavior that could otherwise be added via Modifier. Parameters should allow to set or customize the behavior that exists internally in the component. 498 499**DON’T:** 500``` 501@Composable 502fun Image( 503 bitmap: ImageBitmap, 504 // not core functionality, click can be added via Modifier.clickable 505 onClick: () -> Unit = {}, 506 modifier: Modifier = Modifier, 507 // can be specified via `Modifier.clip(CircleShape)` 508 clipToCircle: Boolean = false 509) 510``` 511 512**Do:** 513``` 514@Composable 515fun Button( 516 onClick: () -> Unit, 517 // modifier param specified so that width, padding etc can be added 518 modifier: Modifier = Modifier, 519 // button is a colored rect that clicks, so background 520 // considered as a core functionality, OK as a param 521 backgroundColor: Color = MaterialTheme.colors.primary 522) 523``` 524 525### `modifier` parameter 526 527Every component that emits UI should have a modifier parameter. Make sure that modifier parameter: 528 529* Has the type `Modifier`. 530 * Type Modifier ensures that any Modifier can be passed to the component. 531* Is the first optional parameter. 532 * If a component has non-zero default size - modifier should be optional, since the component is self sufficient. For components with zero default size modifier parameter can be a required param. 533 * Since the modifier is recommended for any component and is used often, placing it first ensures that it can be set without a named parameter and provides a consistent place for this parameter in any component. 534* Has a no-op default value `Modifier`. 535 * No-op default value ensures that no functionality will be lost when users provide their own modifiers for the component. 536* Is the only parameter of type Modifier in the parameter list. 537 * Since modifiers intend to modify the external behaviors and appearance of the component, one modifier parameter should be sufficient. Consider asking for specific parameters or reconsidering the layering of the component (e.g. breaking component into two) instead. 538* Is applied once as a first modifier in the chain to the root-most layout in the component implementation. 539 * Since modifiers intend to modify the external behaviors and appearance of the component, they must be applied to the outer-most layout and be the first modifiers in the chain. It is ok to chain other modifiers to the modifier that is passed as a parameter. 540 541**Why?** Modifiers are the essential part of compose, users have expectations about their behavior and API. Essentially, modifiers provide a way to modify the external component behavior and appearance, while component implementation will be responsible for the internal behavior and appearance. 542 543**DON’T:** 544``` 545@Composable 546fun Icon( 547 bitmap: ImageBitmap, 548 // no modifier parameter 549 tint: Color = Color.Black 550) 551``` 552 553**DON’T:** 554``` 555@Composable 556fun Icon( 557 bitmap: ImageBitmap, 558 tint: Color = Color.Black, 559 // 1: modifier is not the first optional parameter 560 // 2: padding will be lost as soon as the user sets its own modifier 561 modifier: Modifier = Modifier.padding(8.dp) 562) 563``` 564 565**DON’T:** 566``` 567@Composable 568fun CheckboxRow( 569 checked: Boolean, 570 onCheckedChange: (Boolean) -> Unit, 571 // DON'T - modifier is intended to specify the external behavior of 572 // the CheckboxRow itself, not its subparts. Make them slots instead 573 rowModifier: Modifier = Modifier, 574 checkboxModifier: Modifier = Modifier 575) 576``` 577 578**DON’T:** 579``` 580@Composable 581fun IconButton( 582 buttonBitmap: ImageBitmap, 583 modifier: Modifier = Modifier, 584 tint: Color = Color.Black 585) { 586 Box(Modifier.padding(16.dp)) { 587 Icon( 588 buttonBitmap, 589 // modifier should be applied to the outer-most layout 590 // and be the first one in the chain 591 modifier = Modifier.aspectRatio(1f).then(modifier), 592 tint = tint 593 ) 594 } 595} 596``` 597 598**Do:** 599``` 600@Composable 601fun IconButton( 602 buttonBitmap: ImageBitmap, 603 // good: first optional parameter, single of its kind 604 modifier: Modifier = Modifier, 605 tint: Color = Color.Black 606) { 607 // good: applied before other modifiers to the outer layout 608 Box(modifier.padding(16.dp)) { 609 Icon(buttonBitmap, modifier = Modifier.aspectRatio(1f), tint = tint) 610 } 611} 612``` 613 614**Also Do:** 615``` 616@Composable 617fun ColoredCanvas( 618 // ok: canvas has no intrinsic size, asking for size modifiers 619 modifier: Modifier, 620 color: Color = Color.White, 621 ... 622) { 623 // good: applied before other modifiers to the outer layout 624 Box(modifier.background(color)) { 625 ... 626 } 627} 628``` 629 630### Parameters order 631 632The order of parameters in a component must be as follows: 633 6341. Required parameters. 6352. Single `modifier: Modifier = Modifier`. 6363. Optional parameters. 6374. (optional) trailing `@Composable` lambda. 638 639**Why?** Required parameters indicate the contract of the component, since they have to be passed and are necessary for the component to work properly. By placing required parameters first, API clearly indicates the requirements and contract of the said component. Optional parameters represent some customisation and additional capabilities of the component, and don’t require immediate attention of the user. 640 641Explanation for the order of the parameters: 642 6431. Required parameters. Parameters that don’t have default values and the user is required to pass the values for those parameters in order to use the components. Coming first, they allow users to set them without using named parameters. 6442. `modifier: Modifier = Modifier`. Modifiers should come as a first optional parameter in a @composable function. It must be named `modifier` and have a default value of `Modifier`. There should be only one modifier parameter and it should be applied to the root-most layout in the implementation. See "[modifier parameter](#modifier-parameter)" section for more information. 6453. Optional parameters. Parameters that have default values that will be used if not overridden by the user of the component. Coming after required parameters and a `modifier` parameter, they do not require the user to make an immediate choice and allow one-by-one override using named parameters. 6464. (optional) trailing `@Composable` lambda representing the main content of the component, usually named `content`. It can have a default value. Having non-@composable trailing lambda (e.g. `onClick`) might be misleading as it is a user expectation to have a trailing lambda in a component to be `@Composable`. For `LazyColumn` and other DSL-like exceptions, it is ok to have non-@composable lambda since it still represents the main content. 647 648Think about the order of parameters inside the “required” and “optional” subgroups as well. Similar to the split between required and optional parameters, it is beneficial for the reader and user of the API to see the data, or “what” part of the component first, while metadata, customisation, the “how” of the component should come after. 649 650It makes sense to group parameters semantically within the required or optional groups. If you have a number of color parameters (`backgroundColor` and `contentColor`), consider placing them next to each other to make it easy for the user to see customisation options. 651 652**Do** 653``` 654@Composable 655fun Icon( 656 // image bitmap and contentDescription are required 657 // bitmap goes first since it is the required data for the icon 658 bitmap: ImageBitmap, 659 // contentDescription follows as required, but it is a "metadata", so 660 // it goes after the "data" above. 661 contentDescription: String?, 662 // modifier is the first optional parameter 663 modifier: Modifier = Modifier, 664 // tint is optional, default value uses theme-like composition locals 665 // so it's clear where it's coming from and to change it 666 tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current) 667) 668``` 669 670**Do** 671``` 672@Composable 673fun LazyColumn( 674 // no required parameters beyond content, modifier is the first optional 675 modifier: Modifier = Modifier, 676 // state is important and is a "data": second optional parameter 677 state: LazyListState = rememberLazyListState(), 678 contentPadding: PaddingValues = PaddingValues(0.dp), 679 reverseLayout: Boolean = false, 680 // arrangement and alignment go one-by-one since they are related 681 verticalArrangement: Arrangement.Vertical = 682 if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, 683 horizontalAlignment: Alignment.Horizontal = Alignment.Start, 684 flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), 685 userScrollEnabled: Boolean = true, 686 // trailing lambda with content 687 content: LazyListScope.() -> Unit 688) 689``` 690 691### Nullable parameter 692 693Make conscious choices between the semantical meaning of the parameter or its absence. There’s a difference between default value, empty value and absent value. A conscious choice has to be made when choosing the right semantic for the API. 694 695* Nullability of the parameters should be introduced as a signal to allow users to express “absence” of the parameter and corresponding component’s capabilities. 696* Avoid making parameters nullable to utilize `null` as a “use default in the implementation” signal. 697* Avoid making parameter nullable to signal that the value exists, but is empty, prefer a meaningful empty default value. 698 699**DON’T** 700``` 701@Composable 702fun IconCard( 703 bitmap: ImageBitmap, 704 // avoid having null as a signal to gather default 705 elevation: Dp? = null 706) { 707 // instead of implementation based default resolution, provide meaningful default 708 val resolvedElevation = elevation ?: DefaultElevation 709} 710``` 711 712**Do:** 713``` 714@Composable 715fun IconCard( 716 bitmap: ImageBitmap, 717 elevation: Dp = 8.dp 718) { ... } 719``` 720 721 722**Or Do (null is meaningful here):** 723``` 724@Composable 725fun IconCard( 726 bitmap: ImageBitmap, 727 // null description is NOT the same as "" description 728 // since when it is null - we don't add any accessibility info. 729 contentDescription: String? 730) { ... } 731``` 732 733### Default expressions 734 735Developers should make sure that default expressions on optional parameters are publicly available and meaningful. Best practices: 736 737* Default expression does not contain private/internal calls. This allows users that wrap/extend components to provide the same default. Alternatively, this default can be used by the users in the if statement: `if (condition) default else myUserValue`. 738* Default should have meaningful value, it should be clear what the default value is. Avoid using `null` as a marker to use the default value internally. Refer to null as the “absence” of the value (per "[nullable parameter](#nullable-parameter)" section). Absence of the value (null) is a valid default in this case. 739* Use `ComponentDefaults` objects to name-space defaults values if you have a number of them. 740 741**DON’T** 742``` 743@Composable 744fun IconCard( 745 bitmap: ImageBitmap, 746 //backgroundColor has meaningful default, but it is inaccessible to users 747 backgroundColor: Color = DefaultBackgroundColor, 748 // avoid having null as a signal to gather default 749 elevation: Dp? = null 750) { 751 // instead of implementation based default resolution, provide meaningful default 752 val resolvedElevation = elevation ?: DefaultElevation 753} 754 755// this default expression is private. 756// Users unable to access it when wrapping your component. 757private val DefaultBackgroundColor = Color.Red 758private val DefaultElevation = 8.dp 759``` 760 761**Do:** 762``` 763@Composable 764fun IconCard( 765 bitmap: ImageBitmap, 766 //all params have meaningful defaults that are accessible 767 backgroundColor: Color = IconCardDefaults.BackgroundColor, 768 elevation: Dp = IconCardDefaults.Elevation 769) { ... } 770 771// defaults namespaced in the ComponentNameDefaults object and public 772object IconCardDefaults { 773 val BackgroundColor = Color.Red 774 val Elevation = 8.dp 775} 776``` 777 778**Note:** If your component has a limited number of parameters that have short and predictable defaults (``elevation = 0.dp``), `ComponentDefaults` object might be omitted in favor of simple inline constants. 779 780### MutableState\<T\> as a parameter 781 782Parameters of type `MutableState<T>` are discouraged since it promotes joint ownership over a state between a component and its user. If possible, consider making the component stateless and concede the state change to the caller. If mutation of the parent’s owned property is required in the component, consider creating a `ComponentState` class with the domain specific meaningful field that is backed by `mutableStateOf()`. 783 784When a component accepts `MutableState` as a parameter, it gains the ability to change it. This results in the split ownership of the state, and the usage side that owns the state now has no control over how and when it will be changed from within the component’s implementation. 785 786**DON’T** 787``` 788@Composable 789fun Scroller( 790 offset: MutableState<Float> 791) {} 792``` 793 794**Do (stateless version, if possible):** 795``` 796@Composable 797fun Scroller( 798 offset: Float, 799 onOffsetChange: (Float) -> Unit, 800) {} 801``` 802 803**Or do (state-based component version, if stateless not possible):** 804``` 805class ScrollerState { 806 val offset: Float by mutableStateOf(0f) 807} 808 809@Composable 810fun Scroller( 811 state: ScrollerState 812) {} 813``` 814 815### State\<T\> as a parameter 816 817Parameters of type `State<T> `are discouraged since it unnecessarily narrows the type of objects that can be passed in the function. Given `param: State<Float>`, there are two better alternatives available, depending on the use case: 818 8191. `param: Float`. If the parameter doesn’t change often, or is being read immediately in the component (composition), developers can provide just a plain parameter and recompose the component when it changes. 8202. `param: () -> Float`. To delay reading the value until a later time via `param.invoke()`, lambda might be provided as a parameter. This allows the developers of the component to read the value only when/if it is needed and avoid unnecessary work. For example, if the value is only read during drawing operation, [only redraw will occur](https://developer.android.com/jetpack/compose/phases#state-reads). This leaves the flexibility to the user to provide any expression, including the `State<T>`’s read: 821 1. `param = { myState.value }` - read the `State<T>`’s value 822 2. `param = { justValueWithoutState }` - plain value not backed by the `State<T>` 823 3. `param = { myObject.offset }` - user can have a custom state object where the field (e.g. ``offset``) is backed by the `mutableStateOf()` 824 825**DON’T** 826``` 827fun Badge(position: State<Dp>) {} 828 829// not possible since only State<T> is allowed 830Badge(position = scrollState.offset) // DOES NOT COMPILE 831``` 832 833**Do:** 834``` 835fun Badge(position: () -> Dp) {} 836 837// works ok 838Badge(position = { scrollState.offset }) 839``` 840 841### Slot parameters 842 843#### What are slots 844 845Slot is a `@Composable` lambda parameter that specifies a certain sub hierarchy of the component. Content slot in a Button might look like this: 846``` 847@Composable 848fun Button( 849 onClick: () -> Unit, 850 content: @Composable () -> Unit 851) {} 852 853// usage 854Button(onClick = { /* handle the click */}) { 855 Icon(...) 856} 857``` 858 859This pattern allows the button to have no opinion on the content, while playing the role of drawing the necessary decoration around, handling clicks and showing ripples. 860 861#### Why slots 862 863It might be tempting to write the button as follows: 864 865**DON’T** 866``` 867@Composable 868fun Button( 869 onClick: () -> Unit, 870 text: String? = null, 871 icon: ImageBitmap? = null 872) {} 873``` 874 875Where either text or icon or both are present, leaving the button to arrange the show. While it handles basic use cases or sample usages well, it has some fundamental flexibility flaws: 876 877* **Restricts styling choice:** by using only `String`, Button disallows users to use `AnnotatedString` or other sources of text information, if required. To provide some styling, Button will have to accept `TextStyle` parameters as well, plus some other ones. This will bloat the API of the button quickly. 878* **Restricts component choice:** While Button might want to show a text, `String` might not be enough. If a user has their own `MyTextWithLogging()` component, they might want to use it in a button to do some additional logic like logging events and such. This is impossible with the String API unless the user forks the Button. 879* **Overloads explosion:** If we want some flexibility, for example accepting both ImageBitmap and VectorPainter as icons, we have to provide an overload for that. We can multiply it for every such parameter (`text` being `String` or `AnnotatedString` or `CharSequence`), resulting in the big number of overloads we have to provide in order to cater the users’ use cases. 880* **Restricts component layout capabilities:** In the example above, the Button is opinionated about the arrangement between text and icon. If a user has a special icon that they want to put with a custom arrangement (e.g. on a button’s text baseline or with 4dp additional padding) - they won’t be able to do it. 881 882Slot APIs in components are free from these problems, as a user can pass any component with any styling in a slot. Slots come with the price of simple usages being a bit more verbose, but this downside disappears quickly as soon as a real-application usage begins. 883 884**Do** 885``` 886@Composable 887fun Button( 888 onClick: () -> Unit, 889 text: @Composable () -> Unit, 890 icon: @Composable () -> Unit 891) {} 892``` 893 894#### Single “content” slot overloads 895 896For components that are responsible for layouting of multiple slot APIs it accepts, consider providing an overload with a single slot, usually named `content`. This allows for more flexibility on the usage side when needed as it is possible to change the slot layout logic. 897 898**Do** 899``` 900@Composable 901fun Button( 902 onClick: () -> Unit, 903 content: @Composable () -> Unit 904) {} 905 906// usage 907Button(onClick = { /* handle the click */}) { 908 Row { 909 Icon(...) 910 Text(...) 911 } 912} 913``` 914 915#### Layout strategy scope for slot APIs 916 917If applicable, consider choosing an appropriate layout strategy for the slot lambda. This is especially important for single `content` overloads. In the example above, developers of the Button might notice that most common usage patterns include: single text, single icon, icon and text in a row, text then icon in a row. It might make sense to provide `RowScope` in a content slot, making it easier for the user to use the button 918 919**Do** 920``` 921@Composable 922fun Button( 923 onClick: () -> Unit, 924 content: @Composable RowScope.() -> Unit 925) {} 926 927// usage 928Button(onClick = { /* handle the click */ }) { // this: RowScope 929 Icon(...) 930 Text(...) 931} 932``` 933 934`ColumnScope` or `BoxScope` are good candidates for other types of layout strategies for components. The author of the component SHOULD always think about what will happen if multiple components are passed in a slot and consider communicating this behaviour to a user via scopes (`RowScope` in a Button example above). 935 936#### Lifecycle expectations for slot parameters 937 938Developers should ensure that the lifecycle of the visible and composed slot parameter composables is either the same as the composable that accepts that slot, or is tied to visibility of the slot in the viewport. 939 940`@Composable` components that are passed in the slot should not be disposed of and composed again on the structural or visual changes in the parent component. 941 942If in need to make structural changes internally that affect slot composables lifecycle, use `remember{}` and `movableContentOf()` 943 944**DON’T** 945``` 946@Composable 947fun PreferenceItem( 948 checked: Boolean, 949 content: @Composable () -> Unit 950) { 951 // don't: this logic will dispose and compose again from scratch the content() composable on the `checked` boolean change 952 if (checked) { 953 Row { 954 Text("Checked") 955 content() 956 } 957 } else { 958 Column { 959 Text("Unchecked") 960 content() 961 } 962 } 963} 964``` 965 966**Do** 967``` 968@Composable 969fun PreferenceItem( 970 checked: Boolean, 971 content: @Composable () -> Unit 972) { 973 Layout({ 974 Text("Preference item") 975 content() 976 }) { 977 // custom layout that relayouts the same instance of `content` 978 // when `checked` changes 979 } 980} 981``` 982 983**Or Do** 984``` 985@Composable 986fun PreferenceItem( 987 checked: Boolean, 988 content: @Composable () -> Unit 989) { 990 // this call preserves the lifecycle of `content` between row and column 991 val movableContent = remember(content) { movableContentOf(content)} 992 if (checked) { 993 Row { 994 Text("Checked") 995 movableContent() 996 } 997 } else { 998 Column { 999 Text("Unchecked") 1000 movableContent() 1001 } 1002 } 1003} 1004``` 1005 1006It is expected that slots that become absent from the UI or leave the view port will be disposed of and composed again when they become visible: 1007 1008**Do:** 1009``` 1010@Composable 1011fun PreferenceRow( 1012 checkedContent: @Composable () -> Unit, 1013 checked: Boolean 1014) { 1015 // since checkedContent() is only visible in the checked state 1016 // it is ok for this slot to be disposed when not present 1017 // and be composed again when present again 1018 if (checked) { 1019 Row { 1020 Text("Checked") 1021 checkedContent() 1022 } 1023 } else { 1024 Column { 1025 Text("Unchecked") 1026 } 1027 } 1028} 1029``` 1030 1031#### DSL based slots 1032 1033Avoid DSL based slots and APIs where possible and prefer simple slot `@Composable` lambdas. While giving the developers control over what the user might place in the particular slot, DSL API still restricts the choice of component and layout capabilities. Moreover, the DSL introduces the new API overhead for users to learn and for developers to support. 1034 1035**DON’T** 1036``` 1037@Composable 1038fun TabRow( 1039 tabs: TabRowScope.() -> Unit 1040) {} 1041 1042interface TabRowScope { 1043 // can be a string 1044 fun tab(string: String) 1045 // Can be a @composable as well 1046 fun tab(tabContent: @Composable () -> Unit) 1047} 1048``` 1049 1050Instead of DSL, consider relying on plain slots with parameters. This allows the users to operate with tools they already know while not sacrificing any flexibility. 1051 1052**Do instead:** 1053``` 1054@Composable 1055fun TabRow( 1056 tabs: @Composable () -> Unit 1057) {} 1058 1059@Composable 1060fun Tab(...) {} 1061 1062// usage 1063TabRow { 1064 tabsData.forEach { data -> 1065 Tab(...) 1066 } 1067} 1068``` 1069 1070DSL for defining content of the component or its children should be perceived as an exception. There are some cases that benefit from the DSL approach, notably when the component wants to lazily show and compose only the subset of children (e.g. `LazyRow`, `LazyColumn`). 1071 1072**Allowed, since laziness and flexibility with different data types is needed:** 1073``` 1074@Composable 1075fun LazyColumn( 1076 content: LazyListScope.() -> Unit 1077) {} 1078 1079// usage: DSL is fine since it allows Lazycolumn to lazily compose the subset of children 1080LazyColumn { 1081 // allow to define different types of children and treat them differently 1082 // since sticky header can act both like an item and a sticky header 1083 stickyHeader { 1084 Text("Header") 1085 } 1086 items(...) { 1087 Text($index) 1088 } 1089} 1090``` 1091 1092Even in such cases like with `LazyColumn` it is possible to define the API structure without DSL, so simple version should be considered first 1093 1094**Do. Simpler, easier to learn and use API that still provides laziness of children composition:** 1095``` 1096@Composable 1097fun HorizontalPager( 1098 // pager still lazily composes pages when needed 1099 // but the api is simpler and easier to use; no need for DSL 1100 pageContent: @Composable (pageIndex: Int) -> Unit 1101) {} 1102``` 1103 1104## Component-related classes and functions 1105 1106**Jetpack Compose framework development** MUST follow the rules in this section. 1107 1108**Library development** SHOULD follow the section below. 1109 1110**App development** MAY follow the rules below. 1111 1112### State 1113 1114For core design practices with state, visit [corresponding section in compose api guidelines](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-api-guidelines.md#compose-api-design-patterns). 1115 1116### ComponentDefault object 1117 1118All component default expressions should either be inline or live in the top level object called `ComponentDefaults`, where `Component` is a real component name. Refer to the “[Default expressions](#default-expressions)” section for details. 1119 1120### ComponentColor/ComponentElevation objects 1121 1122Consider a simple if-else expression in the default statements for a simple branching logic, or a dedicated `ComponentColor`/`ComponentElevation` class that clearly defines the inputs that a particular Color/Elevation can be reflected against. 1123 1124There’s a number of ways to provide and/or allow customisation of a certain single type of parameters (e.g. colors, dp) depending on the state of the component (e.g. enabled/disabled, focused/hovered/pressed). 1125 1126**Do (if color choosing logic is simple)** 1127``` 1128@Composable 1129fun Button( 1130 onClick: () -> Unit, 1131 enabled: Boolean = true, 1132 backgroundColor = 1133 if (enabled) ButtonDefaults.enabledBackgroundColor 1134 else ButtonDefaults.disabledBackgroundColor, 1135 elevation = 1136 if (enabled) ButtonDefaults.enabledElevation 1137 else ButtonDefaults.disabledElevation, 1138 content: @Composable RowScope.() -> Unit 1139) {} 1140``` 1141 1142While this works well, those expressions can grow pretty quickly and pollute the API space. That’s why it might be sensible to isolate this to a domain and parameter specific class. 1143 1144**Do (if color conditional logic is more complicated)** 1145``` 1146class ButtonColors( 1147 backgroundColor: Color, 1148 disabledBackgroundColor: Color, 1149 contentColor: Color, 1150 disabledContentColor: Color 1151) { 1152 fun backgroundColor(enabled: Boolean): Color { ... } 1153 1154 fun contentColor(enabled: Boolean): Color { ... } 1155} 1156 1157object ButtonDefaults { 1158 // default factory for the class 1159 // can be @Composable to access the theme composition locals 1160 fun colors( 1161 backgroundColor: Color = ..., 1162 disabledBackgroundColor: Color = ..., 1163 contentColor: Color = ..., 1164 disabledContentColor: Color = ... 1165 ): ButtonColors { ... } 1166} 1167 1168@Composable 1169fun Button( 1170 onClick: () -> Unit, 1171 enabled: Boolean = true, 1172 colors: ButtonColors = ButtonDefaults.colors(), 1173 content: @Composable RowScope.() -> Unit 1174) { 1175 val resolvedBackgroundColor = colors.backgroundColor(enabled) 1176} 1177``` 1178 1179This way, while not introducing the overhead and complexities of the “styles” pattern, we isolate the configuration of a specific part of the component. Additionally, unlike plain default expression, `ComponentColors` or `ComponentElevation` classes allow for more granular control, where the user can specify the enabled and disabled colors/elevation separately. 1180 1181**Note:** This approach is different from styles that are discouraged in compose "[no styles](#prefer-multiple-components-over-style-classes)" chapter for rationale. `ComponentColor` and other such classes target a certain type of functionality of the component, allowing for definition of the color against explicit inputs. The instances of this class must be passed as an explicit parameter for the component. 1182 1183**Note:** While `ComponentColors` and `ComponentElevation` are the most common patterns, there are other component parameters that can be isolated in the similar fashion. 1184 1185## Documentation for the component 1186 1187**Jetpack Compose framework development** SHOULD follow the rules in this section below. 1188 1189**Compose library development** MAY follow the rules in the sections below. 1190 1191**App development** MAY follow. 1192 1193Documentation for `@Composable` components should follow JetBrains’s [ktdoc guidelines and syntax](https://kotlinlang.org/docs/kotlin-doc.html#kdoc-syntax). Additionally, documentation must communicate a component's capabilities to developers via multiple channels: description of the component purpose, parameters and expectations about those parameters, usage examples. 1194 1195### Documentation structure and ordering 1196 1197Every component should have following documentation structure: 1198 11991. One-liner paragraph summarizing the component and what it does. 12002. Paragraphs going more into the detail of components, outlining the capabilities, behavior and might include one or more of: 1201 * `@sample` tag providing an example of the usage for this components and its states, default, etc. If you don't have access to `@sample` functionality, consider inline examples in the ktdoc. 1202 * `@see` tags pointing to other related apis. 1203 * Links to design or other materials to help to use the components to its full potential. 12043. Description for each parameter of the component, starting with `@param paramname`. 1205 * Developers might decide to optionally omit the documentation for the trailing `@Composable` `content` lambda as it is always implied to be the main content slot for the component. 1206 1207### Documentation example 1208 1209**Do** 1210``` 1211/** 1212* Material Design badge box. 1213* 1214* A badge represents dynamic information such as a number of pending requests in a navigation bar. Badges can be icon only or contain short text. 1215* 1216*  1217* 1218* A common use case is to display a badge with navigation bar items. 1219* For more information, see [Navigation Bar](https://m3.material.io/components/navigation-bar/overview) 1220* 1221* A simple icon with badge example looks like: 1222* @sample androidx.compose.material3.samples.NavigationBarItemWithBadge 1223* 1224* @param badge the badge to be displayed - typically a [Badge] 1225* @param modifier the [Modifier] to be applied to this BadgedBox 1226* @param content the anchor to which this badge will be positioned 1227*/ 1228@ExperimentalMaterial3Api 1229@Composable 1230fun BadgedBox( 1231 badge: @Composable BoxScope.() -> Unit, 1232 modifier: Modifier = Modifier, 1233 content: @Composable BoxScope.() -> Unit 1234) 1235``` 1236 1237## Accessibility of the component 1238 1239Consider using foundation building blocks like `Modifier.clickable` or `Image` for better accessibility. Those building blocks will provide good defaults when possible, or will explicitly ask for needed information. Accessibility needs to be manually handled when using ui-level blocks, such as `Layout` or `Modifier.pointerInput`. This section contains best practices regarding accessible API design and accessibility implementation tuning. 1240 1241### Semantics merging 1242 1243Jetpack Compose uses semantics merging for accessibility purposes. This way, `Button` with the content slot doesn’t have to set the text for accessibility service to announce. Instead, the content’s semantics (`Icon`’s contentDescription or `Text`’s text) will be merged into the button. Refer to the [official documentation](https://developer.android.com/jetpack/compose/semantics#merged-vs-unmerged) for more info. 1244 1245To manually create a node that will merge all of its children, you can set a `Modifier.semantics(mergeDescendants = true)` modifier to your component. This will force all non-merging children to collect and pass the data to your component, so it will be treated as a single entity. Some foundation-layer modifiers merge descendants by default (example: `Modifier.clickable` or `Modifier.toggleable`). 1246 1247### Accessibility related parameters 1248 1249For especially common accessibility needs, developers might want to accept some accessibility-related parameters to let users help to provide better accessibility. This is especially true for leaf components like `Image` or `Icon`. `Image` has a required parameter `contentDescription` to signal to the user the need to pass the necessary description for an image. When developing components, developers need to make a conscious decision on what to build in in the implementation vs what to ask from the user via parameters. 1250 1251Note that if you follow the normal best practice of providing an ordinary Modifier parameter and put it on your root layout element, this on its own provides a large amount of implicit accessibility customizability. Because the user of your component can provide their own `Modifier.semantics` which will apply to your component. In addition, this also provides a way for developers to override a portion of your component’s default semantics: if there are two `SemanticsProperties` with identical keys on one modifier chain, Compose resolves the conflict by having the first one win and the later ones ignored. 1252 1253Therefore, you don’t need to add a parameter for every possible semantics your component might need. You should reserve them for especially common cases where it would be inconvenient to write out the `semantics` block every time, or use cases where for some reason the Modifier mechanism doesn’t work (for example, you need to add semantics to an inner child of your component). 1254 1255### Accessibility tuning 1256 1257While basic accessibility capabilities will be granted by using foundation layer building blocks, there’s a potential for developers to make the component more accessible. 1258 1259There are specific semantics expected for individual categories of components: simple components typically require 1-3 semantics, whereas more complex components like text fields, scroll containers or time/date pickers require a very rich set of semantics to function correctly with screenreaders. When developing a new custom component, first consider which of the existing standard Compose components it’s most similar to, and imitating the semantics provided by that component’s implementation, and the exact foundation building blocks it uses. Go from there to fine-tune and add more semantical actions and/or properties when needed. 1260 1261## Tooling support 1262 1263**Jetpack Compose framework development** SHOULD follow the rules in this 1264section below. 1265 1266**Compose library development** MAY follow the rules in the sections below. 1267 1268**App development** MAY follow. 1269 1270Consider component behaviour in app developer tooling including Android Studio 1271Previews and test infrastructure. Components are expected to behave correctly in 1272those environments to make the developer experience productive. 1273 1274### Compose Preview tooling 1275 1276Components are expected to display initial state when used in non-interactive 1277preview mode. 1278 1279Components should avoid patterns that delay the initial render to a subsequent 1280frame. Avoid using LaunchedEffects or asynchronous logic for initial component 1281state set up. 1282 1283If required use `LocalInspectionMode.current` to detect when running as a 1284preview, and do the minimal change to ensure Previews are functional. Avoid 1285replacing a complex component with some placeholder image in Previews. Ensure 1286your component works correctly with various parameters provided via the preview 1287tooling. 1288 1289In interactive mode, Previews should allow direct use of the component with the 1290same interactive experience as when running in an application. 1291 1292### Screenshot testing 1293 1294Components should support screenshot testing. 1295 1296Prefer stateless components where state is passed as a parameter to make sure 1297the component is screenshot-testable in various states. Alternatively, support 1298use of 1299[Compose testing APIs](https://developer.android.com/develop/ui/compose/testing/apis) 1300such as SemanticsMatcher to affect the internal state. 1301 1302Android specific components should ideally support both 1303[Compose Preview Screenshot Testing](https://developer.android.com/studio/preview/compose-screenshot-testing) 1304and Robolectric 1305([RNG](https://github.com/robolectric/robolectric/releases/tag/robolectric-4.10)) 1306to enable effective screenshot testing. 1307 1308## Evolution of the Component APIs 1309 1310**Jetpack Compose framework development** MUST follow the rules in this section below. 1311 1312**Compose library development** MUST follow the rules in the sections below. 1313 1314**App development** MAY follow. 1315 1316Refer to the [kotlin backwards compatibility](https://kotlinlang.org/docs/jvm-api-guidelines-backward-compatibility.html) guidelines for additional information. 1317 1318Since every compose is a function, the following rules apply to the component API changes: 1319 1320* Parameters of the functions MUST NOT be removed. 1321* Newly added parameter to existing functions MUST have default expressions. 1322* New parameters MAY be added as a last parameter, or second to last in cases of trailing lambdas. 1323 * The developer might decide to put the new parameter closer to other parameters that are semantically close to a new one. Keep in mind that this might break source compatibility if the user uses the component without named parameters. 1324 1325The workflow to add a new parameter to a component: 1326 13271. Create a new overload with the new parameter containing the default. 13282. Deprecate the existing function with `DeprecationLevel.Hidden` for binary compatibility. 13293. Make the deprecated version to call your new one. 1330 1331**Do:** 1332``` 1333// existing API we want to extend 1334@Deprecated( 1335 "Maintained for compatibility purposes. Use another overload", 1336 level = DeprecationLevel.HIDDEN 1337) 1338@Composable 1339fun Badge(color: Color) {} 1340 1341// new overload has to be created 1342@Composable 1343fun Badge( 1344 color: Color, 1345 // default should be provided 1346 secondaryColor: Color = Color.Blue 1347) {} 1348``` 1349