PreviewLab
PreviewLab is a powerful preview environment for Compose UI components that enables interactive development and testing. It provides dynamic field controls, event tracking, and multi-device previews to enhance the development experience.
Basic Usage
Use PreviewLab as a wrapper around your preview composables to enable interactive controls:
@Preview
@Composable
private fun MyButtonPreview() = PreviewLab {
val buttonText by fieldState { StringField("Text", "Click Me!") }
val isEnabled by fieldState { BooleanField("Enabled", true) }
val variant by fieldState { EnumField<ButtonVariant>("Variant", ButtonVariant.Primary) }
MyButton(
text = buttonText,
enabled = isEnabled,
variant = variant,
onClick = { onEvent("Button clicked") }
)
}Key Features
Interactive Fields
fieldState: Creates mutable fields for interactive state management
fieldValue: Creates read-only fields for parameter configuration
Built-in field types: StringField, BooleanField, IntField, EnumField, SelectableField, etc.
Event Tracking
onEvent: Records user interactions and displays toast notifications
Events are logged in the Events tab for debugging and testing
Multi-Device Preview
screenSizes: Test components across different screen sizes simultaneously
Built-in presets for smartphones, tablets, and desktop devices
Visual Controls
Inspector Panel: Right sidebar with fields, events, and settings
Zoom and Pan: Scale and move preview content for detailed inspection
Device Frames: Visual device boundaries for accurate sizing
Advanced Usage
Custom PreviewLab Wrapper
Create reusable PreviewLab configurations by wrapping PreviewLab in your own Composable function:
@Composable
fun MyProjectPreviewLab(
modifier: Modifier = Modifier,
content: @Composable PreviewLabScope.() -> Unit,
) = PreviewLab(
modifier = modifier,
state = remember { PreviewLabState() },
screenSizes = ScreenSize.AllPresets,
contentRoot = { c -> MaterialTheme { c() } },
content = content,
)
@Composable
private fun MyComponentPreview() = MyProjectPreviewLab {
MyComponent()
}Complex Field Combinations
Combine multiple fields for comprehensive testing:
PreviewLab {
val theme by fieldState { EnumField<AppTheme>("Theme", AppTheme.Light) }
val language by fieldState { SelectableField<Locale>("Language") {
choice(Locale.ENGLISH, "English", isDefault = true)
choice(Locale.JAPANESE, "日本語")
}}
val isLoading by fieldState { BooleanField("Loading State", false) }
MyApp(theme = theme, locale = language, isLoading = isLoading)
}Parameters
Parameters
Modifier to apply to the PreviewLab container. Can be used to control size, background, padding, or other layout properties of the entire PreviewLab UI.
PreviewLabState instance that manages the preview's configuration and field values. Defaults to a saveable state that persists across recompositions. Override to provide custom state management or sharing state between multiple previews.
Usage examples:
// Default behavior - state persists across recompositions
PreviewLab { ... } // Uses rememberSaveable internally
// Non-persistent state - resets on every recomposition
PreviewLab(
state = remember { PreviewLabState() }
) { ... }List of screen sizes to display in the preview. Controls which device form factors are available for testing. Defaults to ScreenSize.SmartphoneAndDesktops.
Usage examples:
// Default - smartphone and desktop sizes
PreviewLab { ... } // Uses ScreenSize.SmartphoneAndDesktops
// All available presets including tablets
PreviewLab(
screenSizes = ScreenSize.AllPresets
) { ... }
// Only mobile devices for focused testing
PreviewLab(
screenSizes = listOf(ScreenSize.Phone, ScreenSize.Tablet)
) { ... }
// Custom screen sizes for specific requirements
PreviewLab(
screenSizes = listOf(
ScreenSize(360.dp, 640.dp, "Small Phone"),
ScreenSize(1920.dp, 1080.dp, "Full HD Desktop")
)
) { ... }Controls whether the PreviewLab header is visible. When set to false, hides the header controls (scale, inspector panel toggle, etc.) to provide a cleaner preview view. Defaults to true.
Usage examples:
// Default - header is visible
PreviewLab { ... } // Header shown
// Hide header for embedded preview
PreviewLab(
isHeaderShow = false
) {
MyComponent()
}List of tabs to display in the inspector panel. Defaults to InspectorTab.defaults which shows the built-in Fields and Events tabs.
Usage examples:
// Default - Fields and Events tabs only
PreviewLab { ... } // Uses InspectorTab.defaults
// Add custom tabs alongside default tabs
PreviewLab(
inspectorTabs = InspectorTab.defaults + listOf(DebugTab)
) { ... }
// Only custom tabs (no default Fields/Events)
PreviewLab(
inspectorTabs = listOf(CustomTab1, CustomTab2)
) { ... }
// No tabs at all
PreviewLab(
inspectorTabs = emptyList()
) { ... }Wrapper composable that surrounds the entire PreviewLab UI. Useful for providing custom themes, composition locals, or other global configuration.
Usage examples:
// Default - no wrapper, uses PreviewLabTheme only
PreviewLab { ... } // Uses { it() }
// Apply custom Material3 theme
PreviewLab(
contentRoot = { content ->
MaterialTheme(
colorScheme = lightColorScheme(
primary = Color.Red,
onPrimary = Color.Yellow
)
) {
content()
}
}
) { ... }
// Provide composition locals for your design system
PreviewLab(
contentRoot = { content ->
MyDesignSystem {
CompositionLocalProvider(
LocalCustomTypography provides customTypography,
LocalBranding provides myBranding
) {
content()
}
}
}
) { ... }Controls whether PreviewLab UI is enabled. When set to false, only the content is rendered without the PreviewLab wrapper (header, inspector panel, screen size controls, etc.). Defaults to !LocalInspectionMode.current, which means PreviewLab UI is disabled during Android Studio preview and enabled during normal runtime.
Usage examples:
// Default - disabled in Android Studio preview, enabled at runtime
PreviewLab { ... }
// Always enable PreviewLab UI
PreviewLab(
enable = true
) { ... }
// Always disable PreviewLab UI (only content is shown)
PreviewLab(
enable = false
) { ... }Preview content lambda with PreviewLabScope receiver. Within this scope you have access to:
fieldState { ... }: Create mutable state fields
fieldValue { ... }: Create read-only parameter fields
onEvent(title, description?): Log events for tracking and debugging
The content will be rendered within the selected screen size constraints and can use layout modifiers like fillMaxSize() which will be bounded by the selected screen size.
GraphicsLayer for capturing screenshots. Defaults to rememberGraphicsLayer().
See also
State management and persistence
Scope providing field and event functions
Device screen size definitions and presets
Throws
if screenSizes is empty
Convenience overload for single screen size preview.
Use this when you want to test with a specific screen dimension instead of multiple sizes. This is equivalent to calling the main PreviewLab function with a single-item screenSizes list.
@Preview
@Composable
private fun TabletPreview() = PreviewLab(
maxWidth = 1024.dp,
maxHeight = 768.dp
) {
MyResponsiveComponent()
}Parameters
Maximum width constraint for the preview content
Maximum height constraint for the preview content
Modifier to apply to the PreviewLab container
PreviewLabState instance to use for this preview
Controls whether the PreviewLab header is visible
List of tabs to display in the inspector panel
Wrapper composable for custom themes or composition locals
Controls whether PreviewLab UI is enabled. Defaults to !LocalInspectionMode.current
Preview content within PreviewLabScope
See also
Main PreviewLab function with multiple screen size support
Convenience overload for single screen size preview using a ScreenSize object.
Use this when you want to test with a specific screen size preset instead of multiple sizes. This is equivalent to calling the main PreviewLab function with a single-item screenSizes list.
@Preview
@Composable
private fun PhonePreview() = PreviewLab(
screenSize = ScreenSize.Phone
) {
MyResponsiveComponent()
}Parameters
The screen size to use for the preview
Modifier to apply to the PreviewLab container
PreviewLabState instance to use for this preview
Controls whether the PreviewLab header is visible
List of tabs to display in the inspector panel
Wrapper composable for custom themes or composition locals
Controls whether PreviewLab UI is enabled. Defaults to !LocalInspectionMode.current
Preview content within PreviewLabScope
See also
Main PreviewLab function with multiple screen size support
Device screen size definitions and presets