CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-jetbrains-compose-ui--ui-wasm-js

Compose Multiplatform UI library for WebAssembly/JS target - declarative framework for sharing UIs across multiple platforms with Kotlin.

Pending
Overview
Eval results
Files

resource-management.mddocs/

Resource Management

Resource management in Compose Multiplatform for WASM/JS provides an efficient system for loading and managing application assets including images, strings, fonts, and other resources. The system is optimized for web deployment with asynchronous loading, caching, and web-specific optimizations.

Resource Configuration

configureWebResources

Essential function for setting up resource loading in WASM applications.

@OptIn(ExperimentalResourceApi::class)
fun configureWebResources(configure: WebResourcesConfiguration.() -> Unit)

Basic Setup:

@OptIn(ExperimentalResourceApi::class)
fun main() {
    configureWebResources {
        resourcePathMapping { path -> "./$path" }
    }
    CanvasBasedWindow("MyApp") {
        App()
    }
}

Advanced Configuration:

@OptIn(ExperimentalResourceApi::class)
fun main() {
    configureWebResources {
        resourcePathMapping { path -> 
            when {
                path.startsWith("images/") -> "./assets/$path"
                path.startsWith("fonts/") -> "./static/fonts/${path.removePrefix("fonts/")}"
                else -> "./$path"
            }
        }
    }
}

WebResourcesConfiguration

Configuration object for customizing resource loading behavior.

class WebResourcesConfiguration {
    fun resourcePathMapping(mapping: (String) -> String)
}

Image Resources

painterResource

Load image resources asynchronously.

@Composable
@OptIn(ExperimentalResourceApi::class)
fun painterResource(resource: DrawableResource): Painter

Basic Usage:

@OptIn(ExperimentalResourceApi::class)
@Composable
fun ImageExample() {
    Image(
        painter = painterResource(Res.drawable.logo),
        contentDescription = "App logo",
        modifier = Modifier.size(100.dp)
    )
}

Resource Definition:

// Generated resource accessor
object Res {
    object drawable {
        val logo: DrawableResource = DrawableResource("drawable/logo.png")
        val background: DrawableResource = DrawableResource("drawable/background.jpg")
        val icon_user: DrawableResource = DrawableResource("drawable/icon_user.svg")
    }
}

Image Loading States

Handle loading, success, and error states:

@OptIn(ExperimentalResourceApi::class)
@Composable
fun AsyncImageExample() {
    var imageState by remember { mutableStateOf<ImageLoadState>(ImageLoadState.Loading) }
    
    Box(modifier = Modifier.size(200.dp)) {
        when (imageState) {
            is ImageLoadState.Loading -> {
                CircularProgressIndicator(
                    modifier = Modifier.align(Alignment.Center)
                )
            }
            is ImageLoadState.Success -> {
                Image(
                    painter = painterResource(Res.drawable.photo),
                    contentDescription = "Photo",
                    modifier = Modifier.fillMaxSize(),
                    contentScale = ContentScale.Crop
                )
            }
            is ImageLoadState.Error -> {
                Icon(
                    imageVector = Icons.Default.Error,
                    contentDescription = "Error loading image",
                    modifier = Modifier.align(Alignment.Center),
                    tint = MaterialTheme.colors.error
                )
            }
        }
    }
}

sealed class ImageLoadState {
    object Loading : ImageLoadState()
    object Success : ImageLoadState()
    data class Error(val exception: Throwable) : ImageLoadState()
}

Supported Image Formats

  • PNG: Full transparency support
  • JPEG: Optimized compression
  • SVG: Vector graphics with scaling
  • WebP: Modern web format (browser dependent)
  • GIF: Static images (animated GIF support limited)

String Resources

stringResource

Load localized string resources.

@Composable
@OptIn(ExperimentalResourceApi::class)
fun stringResource(resource: StringResource): String

Usage:

@OptIn(ExperimentalResourceApi::class)
@Composable
fun LocalizedText() {
    Text(
        text = stringResource(Res.string.welcome_message),
        style = MaterialTheme.typography.h4
    )
}

String Resource Definition:

// Generated resource accessor
object Res {
    object string {
        val welcome_message: StringResource = StringResource("string/welcome_message")
        val button_continue: StringResource = StringResource("string/button_continue")
        val error_network: StringResource = StringResource("string/error_network")
    }
}

Parameterized Strings

Handle strings with parameters:

@OptIn(ExperimentalResourceApi::class)
@Composable
fun ParameterizedString(userName: String, count: Int) {
    Text(
        text = stringResource(
            Res.string.welcome_user,
            userName,
            count
        )
    )
}

Pluralization Support

@OptIn(ExperimentalResourceApi::class)
@Composable
fun PluralString(itemCount: Int) {
    Text(
        text = pluralStringResource(
            Res.plurals.items_count,
            itemCount,
            itemCount
        )
    )
}

Font Resources

fontResource

Load custom fonts for typography.

@Composable
@OptIn(ExperimentalResourceApi::class)
fun fontResource(resource: FontResource): androidx.compose.ui.text.font.Font

Usage:

@OptIn(ExperimentalResourceApi::class)
@Composable
fun CustomFontExample() {
    val customFont = FontFamily(
        fontResource(Res.font.roboto_regular),
        fontResource(Res.font.roboto_bold)
    )
    
    Text(
        text = "Custom Font Text",
        fontFamily = customFont,
        fontWeight = FontWeight.Bold
    )
}

Font Family Creation:

@OptIn(ExperimentalResourceApi::class)
val AppFontFamily = FontFamily(
    Font(
        resource = Res.font.opensans_light,
        weight = FontWeight.Light
    ),
    Font(
        resource = Res.font.opensans_regular,
        weight = FontWeight.Normal
    ),
    Font(
        resource = Res.font.opensans_medium,
        weight = FontWeight.Medium
    ),
    Font(
        resource = Res.font.opensans_bold,
        weight = FontWeight.Bold
    )
)

Supported Font Formats

  • TTF: TrueType fonts
  • OTF: OpenType fonts
  • WOFF: Web Open Font Format
  • WOFF2: Web Open Font Format 2.0 (preferred for web)

Raw Resources

Access Raw Files

Load arbitrary resource files:

@OptIn(ExperimentalResourceApi::class)
suspend fun readResourceBytes(resource: Resource): ByteArray

@OptIn(ExperimentalResourceApi::class)
suspend fun readResourceText(resource: Resource): String

Usage:

@OptIn(ExperimentalResourceApi::class)
@Composable
fun ConfigLoader() {
    var config by remember { mutableStateOf<String?>(null) }
    
    LaunchedEffect(Unit) {
        config = readResourceText(Res.files.config_json)
    }
    
    config?.let { configText ->
        // Parse and use configuration
        val configData = Json.decodeFromString<Config>(configText)
        ConfigDisplay(configData)
    }
}

Resource Organization

Directory Structure

src/commonMain/composeResources/
├── drawable/
│   ├── logo.png
│   ├── background.jpg
│   └── icons/
│       ├── user.svg
│       └── settings.png
├── font/
│   ├── roboto-regular.ttf
│   ├── roboto-bold.ttf
│   └── opensans.woff2
├── values/
│   ├── strings.xml
│   └── strings-es.xml
└── files/
    ├── config.json
    └── data.csv

Resource Naming

Naming Conventions:

  • Use lowercase with underscores: user_profile.png
  • Group related resources: icon_home.svg, icon_settings.svg
  • Include size indicators: logo_small.png, logo_large.png
  • Use descriptive names: background_login.jpg, button_primary.png

Localization

Multi-language Support

String Resources (values/strings.xml):

<resources>
    <string name="app_name">My App</string>
    <string name="welcome_message">Welcome to the app!</string>
    <string name="button_continue">Continue</string>
</resources>

Spanish Resources (values-es/strings.xml):

<resources>
    <string name="app_name">Mi Aplicación</string>
    <string name="welcome_message">¡Bienvenido a la aplicación!</string>
    <string name="button_continue">Continuar</string>
</resources>

Language Detection

@Composable
fun LocalizedApp() {
    val currentLanguage = getCurrentLanguage()
    
    // Resources automatically load based on system language
    Text(stringResource(Res.string.welcome_message))
}

fun getCurrentLanguage(): String {
    return kotlinx.browser.window.navigator.language
}

Caching and Performance

Browser Caching

Resources are automatically cached by the browser:

  • Images: Cached with ETags and cache headers
  • Fonts: Long-term browser caching
  • Strings: Loaded once and cached in memory

Preloading Strategies

Critical Resources:

@OptIn(ExperimentalResourceApi::class)
@Composable
fun PreloadCriticalResources() {
    LaunchedEffect(Unit) {
        // Preload critical images
        painterResource(Res.drawable.logo)
        painterResource(Res.drawable.splash_background)
        
        // Preload fonts
        fontResource(Res.font.primary_font)
    }
}

Lazy Loading:

@OptIn(ExperimentalResourceApi::class)
@Composable
fun LazyImageGrid(images: List<ImageResource>) {
    LazyColumn {
        items(images) { imageResource ->
            // Images load only when needed
            AsyncImage(
                resource = imageResource,
                contentDescription = null,
                modifier = Modifier.fillMaxWidth()
            )
        }
    }
}

Error Handling

Resource Loading Errors

@OptIn(ExperimentalResourceApi::class)
@Composable
fun RobustImageLoading() {
    var hasError by remember { mutableStateOf(false) }
    
    if (hasError) {
        // Fallback content
        Box(
            modifier = Modifier.size(100.dp),
            contentAlignment = Alignment.Center
        ) {
            Icon(
                imageVector = Icons.Default.BrokenImage,
                contentDescription = "Image not available"
            )
        }
    } else {
        Image(
            painter = painterResource(Res.drawable.user_photo),
            contentDescription = "User photo",
            modifier = Modifier.size(100.dp),
            onError = { hasError = true }
        )
    }
}

Network Failures

@OptIn(ExperimentalResourceApi::class)
@Composable
fun NetworkAwareResourceLoading() {
    var isOnline by remember { mutableStateOf(true) }
    
    LaunchedEffect(Unit) {
        // Monitor network status
        isOnline = checkNetworkStatus()
    }
    
    if (isOnline) {
        Image(painterResource(Res.drawable.online_content))
    } else {
        // Show cached or offline content
        Image(painterResource(Res.drawable.offline_placeholder))
        Text("Offline mode")
    }
}

WASM-Specific Considerations

Bundle Size Optimization

  • Image compression: Use WebP and optimized formats
  • Font subsetting: Include only required character sets
  • Tree shaking: Unused resources are automatically excluded
  • Compression: Resources are compressed in the build process

Loading Performance

  • Async loading: All resources load asynchronously
  • Streaming: Large resources can be streamed
  • Parallel loading: Multiple resources load simultaneously
  • Progressive enhancement: App starts before all resources load

Memory Management

  • Automatic cleanup: Browser manages resource memory
  • Cache limits: Browser enforces cache size limits
  • Manual cleanup: Use DisposableEffect for large resources
@OptIn(ExperimentalResourceApi::class)
@Composable
fun LargeResourceManager() {
    DisposableEffect(Unit) {
        val largeResource = loadLargeResource()
        
        onDispose {
            // Cleanup if needed
            largeResource.dispose()
        }
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-jetbrains-compose-ui--ui-wasm-js

docs

browser-integration.md

index.md

material-design.md

resource-management.md

state-management.md

ui-components.md

window-management.md

tile.json