Skip to content

Repository files navigation

ComposeGuard

JetBrains Plugin Downloads License Profile Android Weekly

Catch Jetpack Compose mistakes as you type β€” 39 best-practice rules from the Compose Rules guidelines, surfaced live in Android Studio & IntelliJ IDEA with inline highlights, gutter icons, and one-click fixes.


Preview

ComposeGuard Preview

Why ComposeGuard?

The Compose Rules catch the subtle mistakes that hurt Compose code β€” missing Modifier parameters, un-remembered state, unstable collections, reused modifiers, and dozens more. ComposeGuard brings those checks into the editor, so you fix them while the code is still fresh instead of discovering them in a build log or a code review.

  • ⚑ Instant β€” analysis runs as you type; no build, no Gradle task, no CI round-trip.
  • 🎯 Accurate β€” rules are PSI-based and tuned to avoid false positives on valid patterns (overrides, scoped slots, mutually-exclusive branches, run-once effects, …).
  • πŸ›  Actionable β€” most violations come with a quick fix (Alt+Enter) and a detailed explanation of why it matters.
  • 🎚 Configurable β€” enable/disable any rule or whole category, or suppress per declaration.

Table of Contents

Features

  • Real-time highlighting β€” violations appear as colored underlines while you edit.
  • Gutter icons β€” a color-coded dot per @Composable summarizes its status at a glance:
    • πŸ”΄ Error Β Β  🟠 Warning Β Β  βšͺ Weak warning Β Β  πŸ”΅ Info
  • Inline hints β€” compact badges next to function names show rule violations.
  • Hover tooltips β€” every violation explains the problem, the reasoning, and the fix.
  • Quick fixes β€” rename, add a modifier parameter, wrap in remember, switch to a type-specific state, make a preview private, swap to an immutable collection, and more.
  • 39 rules across 6 categories β€” see the full Rule Reference.

Installation

Install from Marketplace

  1. Open Android Studio or IntelliJ IDEA.
  2. Go to Settings β†’ Plugins β†’ Marketplace.
  3. Search for ComposeGuard.
  4. Click Install and restart when prompted.

Or install directly from the JetBrains Marketplace.

Quick Start

Once installed, ComposeGuard automatically analyzes any Kotlin file containing @Composable functions β€” no configuration required. Here are a few things it catches:

// 🟠 Naming: Unit-returning composables should be PascalCase
@Composable
fun userCard(user: User) { }              // β†’ rename to "UserCard"

// 🟠 Modifier: public UI composables should expose a Modifier
@Composable
fun ProductCard(product: Product) {       // β†’ add `modifier: Modifier = Modifier`
    Column { Text(product.name) }
}

// πŸ”΄ State: state must be remembered
@Composable
fun Counter() {
    val count = mutableStateOf(0)         // β†’ wrap in remember { }
}

// 🟠 Stricter: prefer stable collections
@Composable
fun ItemList(items: List<Item>) { }       // β†’ use ImmutableList<Item>

Hover any highlight for the full explanation, or press Alt+Enter to apply a fix.

Suppressing Rules

To intentionally allow a violation, annotate the declaration with @Suppress using the rule id (the same id shown in the warning, e.g. ModifierRequired). The quick fix can insert this for you:

@Suppress("ModifierRequired")
@Composable
fun SplashLogo() {
    Image(painterResource(R.drawable.logo), contentDescription = null)
}

// Multiple rules at once:
@Suppress("ModifierRequired", "ComposableNaming")
@Composable
fun splash() { /* ... */ }

Suppression works at the function, property, or class level. To turn rules off project-wide instead, use Configuration.

Rule Reference

ComposeGuard ships 39 rules based on the Compose Rules guidelines. Severity legend: πŸ”΄ Error Β· 🟠 Warning Β· βšͺ Weak warning Β· πŸ”΅ Info.

Naming

Rule id Checks Severity
ComposableNaming Unit-returning composables use PascalCase; value-returning use camelCase 🟠
CompositionLocalNaming CompositionLocal properties are prefixed with Local 🟠
PreviewNaming @Preview functions reference Preview in their name βšͺ
MultipreviewNaming Multipreview annotation classes reference Preview βšͺ
ComposableAnnotationNaming @ComposableTargetMarker annotations end with Composable βšͺ
EventParameterNaming Event lambdas use present tense (onClick, not onClicked) βšͺ

Modifiers

Rule id Checks Severity
ModifierRequired Public, UI-emitting composables expose a Modifier parameter 🟠
ModifierDefaultValue modifier parameters default to Modifier 🟠
ModifierNaming The main modifier is named modifier; others follow xModifier βšͺ
ModifierTopMost The modifier is applied to the root-most layout 🟠
ModifierReuse The same modifier isn't applied to multiple live nodes 🟠
ModifierOrder Modifier chain order is intentional (e.g. padding before clickable) 🟠
AvoidComposed Prefer Modifier.Node over the deprecated composed { } factory 🟠

State

Rule id Checks Severity
RememberState mutableStateOf and friends are wrapped in remember { } πŸ”΄
TypeSpecificState Primitives use mutableIntStateOf / mutableFloatStateOf / … 🟠
DerivedStateOfCandidate Values computed from state use derivedStateOf 🟠
FrequentRecomposition Hot observable sources use lifecycle-aware collection 🟠
DeferStateReads Fast-changing state reads are deferred to lambda modifiers 🟠
HoistState State is hoisted to the appropriate level πŸ”΅
MutableStateParameter Pass value + callback instead of a MutableState parameter 🟠

Parameters

Rule id Checks Severity
ParameterOrdering Order is required β†’ modifier β†’ optional β†’ trailing content βšͺ
TrailingLambda The content slot is the trailing lambda; event handlers are not βšͺ
MutableParameter Avoid inherently mutable types (MutableList, ArrayList, …) as parameters 🟠
ExplicitDependencies Make injected ViewModels explicit parameters βšͺ
ViewModelForwarding Don't forward a ViewModel into another composable 🟠

Composables & Effects

Rule id Checks Severity
ContentEmission A composable emits content or returns a value, not both 🟠
MultipleContentEmitters A composable emits a single piece of content 🟠
ContentSlotReused A content slot isn't invoked more than once on the same pass 🟠
EffectKeys Changing captured values are passed as effect keys 🟠
LambdaParameterInEffect Lambda parameters used in effects are wrapped in rememberUpdatedState 🟠
MovableContent movableContentOf is remembered πŸ”΄
PreviewVisibility @Preview composables are private 🟠
ComponentDefaultsVisibility A <Component>Defaults object matches its composable's visibility 🟠
LazyListMissingKey Lazy list items provide a stable key πŸ”΅
ComposableNestingDepth Composables are not nested deeper than the configured limit (opt-in) βšͺ
LazyListContentType Heterogeneous lazy lists set a contentType πŸ”΅

Stricter

Rule id Checks Severity
UnstableCollections Prefer ImmutableList / PersistentList over List, Set, Map 🟠
CompositionLocalAllowlist Custom CompositionLocals are declared only when allowlisted (opt-in) 🟠
Material2Usage Migrate androidx.compose.material (M2) imports to Material 3 πŸ”΅

Suppress any rule with @Suppress("<RuleId>"), or toggle it in Settings β†’ Tools β†’ ComposeGuard.

Statistics Dashboard

ComposeGuard includes a tool window that tracks rule violations across your project.

ComposeGuard Statistics Dashboard

  • On-demand project scan β€” press Scan Project to count violations across every Kotlin file.
  • Category breakdown β€” see violations grouped by rule category.
  • Rule-level details β€” drill into specific rules.
  • Project overview β€” track overall code-quality trends.
  • Export β€” save the last scan as JSON or SARIF for CI dashboards and code-scanning tools.

Open it from View β†’ Tool Windows β†’ ComposeGuard, or click a ComposeGuard gutter icon.

Configuration

Configure ComposeGuard at Settings β†’ Tools β†’ ComposeGuard.

ComposeGuard Settings - Disable Rules

  • Enable All Rules β€” master switch that selects or clears every rule at once.
  • Display options β€” toggle gutter icons and inlay hints.
  • Rule configuration β€” enable/disable individual rules or entire categories.
  • Analyze test sources β€” uncheck to leave test source roots alone.

Project configuration (.editorconfig)

ComposeGuard reads the same compose_* keys that the upstream Compose Rules ktlint ruleset uses, so a team can commit one .editorconfig and share it between the IDE plugin and CI. Keys are read from the nearest .editorconfig (walking up to the one marked root = true) in any section that applies to Kotlin files, for example [*.{kt,kts}]. Lists are comma-separated; entries containing regex metacharacters are treated as regular expressions.

Key Affects Meaning
compose_allowed_composable_function_names ComposableNaming Names (or regexes) exempt from the PascalCase/camelCase check
compose_content_emitters ContentEmission, MultipleContentEmitters, ModifierRequired, TrailingLambda Extra composables that count as emitting UI
compose_content_emitters_denylist same as above Composables that must never count as emitting UI
compose_check_modifiers_for_visibility ModifierRequired only_public (default), public_and_internal, or all
compose_modifier_missing_ignore_annotated ModifierRequired Annotation names whose composables are skipped
compose_custom_modifiers ModifierRequired, ModifierNaming, ModifierDefaultValue, ParameterOrdering, TrailingLambda Extra types treated as Modifier (e.g. GlanceModifier)
compose_treat_as_lambda ParameterOrdering, TrailingLambda Type aliases treated as plain lambdas
compose_treat_as_composable_lambda ParameterOrdering, TrailingLambda Type aliases treated as @Composable content slots
compose_view_model_factories ExplicitDependencies Extra ViewModel factory functions
compose_allowed_composition_locals ExplicitDependencies, CompositionLocalAllowlist CompositionLocals that may be read or declared
compose_allowed_state_holder_names ViewModelForwarding Type name regexes that are not treated as forwarded ViewModels
compose_allowed_forwarding ViewModelForwarding Composables a ViewModel may be forwarded to
compose_allowed_forwarding_of_types ViewModelForwarding ViewModel types that may be forwarded
compose_allowed_from_m2 Material2Usage Material 2 imports (or package prefixes) that are allowed
compose_allowed_lambda_parameter_names EventParameterNaming Event parameter names exempt from the present-tense check
compose_preview_naming_strategy PreviewNaming anywhere (default), suffix, or prefix
compose_composable_nesting_depth_threshold ComposableNestingDepth Maximum nesting depth (default 3)
compose_disallow_material2, compose_disallow_unstable_collections, compose_preview_naming_enabled, compose_composable_nesting_depth_enabled rule enablement true turns the rule on for this project regardless of IDE settings
root = true

[*.{kt,kts}]
compose_allowed_from_m2 = androidx.compose.material.icons
compose_treat_as_composable_lambda = Slot
compose_composable_nesting_depth_enabled = true
compose_composable_nesting_depth_threshold = 4

Adopting ComposeGuard in an existing codebase

Adding the plugin to a large legacy project? Roll it out gradually instead of facing every warning at once:

  1. Start with the Stricter category off (Material2Usage, UnstableCollections).
  2. Enable categories one at a time as you refactor β€” the category checkbox toggles the whole group.
  3. Use @Suppress("<RuleId>") for individual, intentional exceptions.

Requirements & Compatibility

  • IntelliJ IDEA 2024.2+ or Android Studio Ladybug (2024.2)+
  • The bundled Kotlin plugin (enabled by default)
ComposeGuard Supported IDE builds
1.2.x 2024.2 – 2026.2

Contributing

Contributions are welcome β€” issues and pull requests both.

  1. Fork the repository.
  2. Create a feature branch: git checkout -b feature/amazing-feature.
  3. Make your change and add tests (./gradlew :compose-guard:test).
  4. Commit and push, then open a Pull Request.

Credits

Built on the excellent Compose Rules guidelines by Nacho Lopez (mrmans0n).

Find this repository useful? ❀️

Support it by joining stargazers for this repository. ⭐
Also, follow me on GitHub for my next creations! 🀩

License

Designed and developed by 2025 androidpoet (Ranbir Singh)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Made with ❀️ by androidpoet

About

Real-time detection of Jetpack Compose best practices and rule violations directly in Android Studio.

Topics

Resources

Stars

117 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages