0
# Resource Management
1
2
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.
3
4
## Resource Configuration
5
6
### configureWebResources
7
8
Essential function for setting up resource loading in WASM applications.
9
10
```kotlin { .api }
11
@OptIn(ExperimentalResourceApi::class)
12
fun configureWebResources(configure: WebResourcesConfiguration.() -> Unit)
13
```
14
15
**Basic Setup:**
16
```kotlin { .api }
17
@OptIn(ExperimentalResourceApi::class)
18
fun main() {
19
configureWebResources {
20
resourcePathMapping { path -> "./$path" }
21
}
22
CanvasBasedWindow("MyApp") {
23
App()
24
}
25
}
26
```
27
28
**Advanced Configuration:**
29
```kotlin { .api }
30
@OptIn(ExperimentalResourceApi::class)
31
fun main() {
32
configureWebResources {
33
resourcePathMapping { path ->
34
when {
35
path.startsWith("images/") -> "./assets/$path"
36
path.startsWith("fonts/") -> "./static/fonts/${path.removePrefix("fonts/")}"
37
else -> "./$path"
38
}
39
}
40
}
41
}
42
```
43
44
### WebResourcesConfiguration
45
46
Configuration object for customizing resource loading behavior.
47
48
```kotlin { .api }
49
class WebResourcesConfiguration {
50
fun resourcePathMapping(mapping: (String) -> String)
51
}
52
```
53
54
## Image Resources
55
56
### painterResource
57
58
Load image resources asynchronously.
59
60
```kotlin { .api }
61
@Composable
62
@OptIn(ExperimentalResourceApi::class)
63
fun painterResource(resource: DrawableResource): Painter
64
```
65
66
**Basic Usage:**
67
```kotlin { .api }
68
@OptIn(ExperimentalResourceApi::class)
69
@Composable
70
fun ImageExample() {
71
Image(
72
painter = painterResource(Res.drawable.logo),
73
contentDescription = "App logo",
74
modifier = Modifier.size(100.dp)
75
)
76
}
77
```
78
79
**Resource Definition:**
80
```kotlin { .api }
81
// Generated resource accessor
82
object Res {
83
object drawable {
84
val logo: DrawableResource = DrawableResource("drawable/logo.png")
85
val background: DrawableResource = DrawableResource("drawable/background.jpg")
86
val icon_user: DrawableResource = DrawableResource("drawable/icon_user.svg")
87
}
88
}
89
```
90
91
### Image Loading States
92
93
Handle loading, success, and error states:
94
95
```kotlin { .api }
96
@OptIn(ExperimentalResourceApi::class)
97
@Composable
98
fun AsyncImageExample() {
99
var imageState by remember { mutableStateOf<ImageLoadState>(ImageLoadState.Loading) }
100
101
Box(modifier = Modifier.size(200.dp)) {
102
when (imageState) {
103
is ImageLoadState.Loading -> {
104
CircularProgressIndicator(
105
modifier = Modifier.align(Alignment.Center)
106
)
107
}
108
is ImageLoadState.Success -> {
109
Image(
110
painter = painterResource(Res.drawable.photo),
111
contentDescription = "Photo",
112
modifier = Modifier.fillMaxSize(),
113
contentScale = ContentScale.Crop
114
)
115
}
116
is ImageLoadState.Error -> {
117
Icon(
118
imageVector = Icons.Default.Error,
119
contentDescription = "Error loading image",
120
modifier = Modifier.align(Alignment.Center),
121
tint = MaterialTheme.colors.error
122
)
123
}
124
}
125
}
126
}
127
128
sealed class ImageLoadState {
129
object Loading : ImageLoadState()
130
object Success : ImageLoadState()
131
data class Error(val exception: Throwable) : ImageLoadState()
132
}
133
```
134
135
### Supported Image Formats
136
137
- **PNG**: Full transparency support
138
- **JPEG**: Optimized compression
139
- **SVG**: Vector graphics with scaling
140
- **WebP**: Modern web format (browser dependent)
141
- **GIF**: Static images (animated GIF support limited)
142
143
## String Resources
144
145
### stringResource
146
147
Load localized string resources.
148
149
```kotlin { .api }
150
@Composable
151
@OptIn(ExperimentalResourceApi::class)
152
fun stringResource(resource: StringResource): String
153
```
154
155
**Usage:**
156
```kotlin { .api }
157
@OptIn(ExperimentalResourceApi::class)
158
@Composable
159
fun LocalizedText() {
160
Text(
161
text = stringResource(Res.string.welcome_message),
162
style = MaterialTheme.typography.h4
163
)
164
}
165
```
166
167
**String Resource Definition:**
168
```kotlin { .api }
169
// Generated resource accessor
170
object Res {
171
object string {
172
val welcome_message: StringResource = StringResource("string/welcome_message")
173
val button_continue: StringResource = StringResource("string/button_continue")
174
val error_network: StringResource = StringResource("string/error_network")
175
}
176
}
177
```
178
179
### Parameterized Strings
180
181
Handle strings with parameters:
182
183
```kotlin { .api }
184
@OptIn(ExperimentalResourceApi::class)
185
@Composable
186
fun ParameterizedString(userName: String, count: Int) {
187
Text(
188
text = stringResource(
189
Res.string.welcome_user,
190
userName,
191
count
192
)
193
)
194
}
195
```
196
197
### Pluralization Support
198
199
```kotlin { .api }
200
@OptIn(ExperimentalResourceApi::class)
201
@Composable
202
fun PluralString(itemCount: Int) {
203
Text(
204
text = pluralStringResource(
205
Res.plurals.items_count,
206
itemCount,
207
itemCount
208
)
209
)
210
}
211
```
212
213
## Font Resources
214
215
### fontResource
216
217
Load custom fonts for typography.
218
219
```kotlin { .api }
220
@Composable
221
@OptIn(ExperimentalResourceApi::class)
222
fun fontResource(resource: FontResource): androidx.compose.ui.text.font.Font
223
```
224
225
**Usage:**
226
```kotlin { .api }
227
@OptIn(ExperimentalResourceApi::class)
228
@Composable
229
fun CustomFontExample() {
230
val customFont = FontFamily(
231
fontResource(Res.font.roboto_regular),
232
fontResource(Res.font.roboto_bold)
233
)
234
235
Text(
236
text = "Custom Font Text",
237
fontFamily = customFont,
238
fontWeight = FontWeight.Bold
239
)
240
}
241
```
242
243
**Font Family Creation:**
244
```kotlin { .api }
245
@OptIn(ExperimentalResourceApi::class)
246
val AppFontFamily = FontFamily(
247
Font(
248
resource = Res.font.opensans_light,
249
weight = FontWeight.Light
250
),
251
Font(
252
resource = Res.font.opensans_regular,
253
weight = FontWeight.Normal
254
),
255
Font(
256
resource = Res.font.opensans_medium,
257
weight = FontWeight.Medium
258
),
259
Font(
260
resource = Res.font.opensans_bold,
261
weight = FontWeight.Bold
262
)
263
)
264
```
265
266
### Supported Font Formats
267
268
- **TTF**: TrueType fonts
269
- **OTF**: OpenType fonts
270
- **WOFF**: Web Open Font Format
271
- **WOFF2**: Web Open Font Format 2.0 (preferred for web)
272
273
## Raw Resources
274
275
### Access Raw Files
276
277
Load arbitrary resource files:
278
279
```kotlin { .api }
280
@OptIn(ExperimentalResourceApi::class)
281
suspend fun readResourceBytes(resource: Resource): ByteArray
282
283
@OptIn(ExperimentalResourceApi::class)
284
suspend fun readResourceText(resource: Resource): String
285
```
286
287
**Usage:**
288
```kotlin { .api }
289
@OptIn(ExperimentalResourceApi::class)
290
@Composable
291
fun ConfigLoader() {
292
var config by remember { mutableStateOf<String?>(null) }
293
294
LaunchedEffect(Unit) {
295
config = readResourceText(Res.files.config_json)
296
}
297
298
config?.let { configText ->
299
// Parse and use configuration
300
val configData = Json.decodeFromString<Config>(configText)
301
ConfigDisplay(configData)
302
}
303
}
304
```
305
306
## Resource Organization
307
308
### Directory Structure
309
310
```
311
src/commonMain/composeResources/
312
├── drawable/
313
│ ├── logo.png
314
│ ├── background.jpg
315
│ └── icons/
316
│ ├── user.svg
317
│ └── settings.png
318
├── font/
319
│ ├── roboto-regular.ttf
320
│ ├── roboto-bold.ttf
321
│ └── opensans.woff2
322
├── values/
323
│ ├── strings.xml
324
│ └── strings-es.xml
325
└── files/
326
├── config.json
327
└── data.csv
328
```
329
330
### Resource Naming
331
332
**Naming Conventions:**
333
- Use lowercase with underscores: `user_profile.png`
334
- Group related resources: `icon_home.svg`, `icon_settings.svg`
335
- Include size indicators: `logo_small.png`, `logo_large.png`
336
- Use descriptive names: `background_login.jpg`, `button_primary.png`
337
338
## Localization
339
340
### Multi-language Support
341
342
**String Resources (values/strings.xml):**
343
```xml
344
<resources>
345
<string name="app_name">My App</string>
346
<string name="welcome_message">Welcome to the app!</string>
347
<string name="button_continue">Continue</string>
348
</resources>
349
```
350
351
**Spanish Resources (values-es/strings.xml):**
352
```xml
353
<resources>
354
<string name="app_name">Mi Aplicación</string>
355
<string name="welcome_message">¡Bienvenido a la aplicación!</string>
356
<string name="button_continue">Continuar</string>
357
</resources>
358
```
359
360
### Language Detection
361
362
```kotlin { .api }
363
@Composable
364
fun LocalizedApp() {
365
val currentLanguage = getCurrentLanguage()
366
367
// Resources automatically load based on system language
368
Text(stringResource(Res.string.welcome_message))
369
}
370
371
fun getCurrentLanguage(): String {
372
return kotlinx.browser.window.navigator.language
373
}
374
```
375
376
## Caching and Performance
377
378
### Browser Caching
379
380
Resources are automatically cached by the browser:
381
- **Images**: Cached with ETags and cache headers
382
- **Fonts**: Long-term browser caching
383
- **Strings**: Loaded once and cached in memory
384
385
### Preloading Strategies
386
387
**Critical Resources:**
388
```kotlin { .api }
389
@OptIn(ExperimentalResourceApi::class)
390
@Composable
391
fun PreloadCriticalResources() {
392
LaunchedEffect(Unit) {
393
// Preload critical images
394
painterResource(Res.drawable.logo)
395
painterResource(Res.drawable.splash_background)
396
397
// Preload fonts
398
fontResource(Res.font.primary_font)
399
}
400
}
401
```
402
403
**Lazy Loading:**
404
```kotlin { .api }
405
@OptIn(ExperimentalResourceApi::class)
406
@Composable
407
fun LazyImageGrid(images: List<ImageResource>) {
408
LazyColumn {
409
items(images) { imageResource ->
410
// Images load only when needed
411
AsyncImage(
412
resource = imageResource,
413
contentDescription = null,
414
modifier = Modifier.fillMaxWidth()
415
)
416
}
417
}
418
}
419
```
420
421
## Error Handling
422
423
### Resource Loading Errors
424
425
```kotlin { .api }
426
@OptIn(ExperimentalResourceApi::class)
427
@Composable
428
fun RobustImageLoading() {
429
var hasError by remember { mutableStateOf(false) }
430
431
if (hasError) {
432
// Fallback content
433
Box(
434
modifier = Modifier.size(100.dp),
435
contentAlignment = Alignment.Center
436
) {
437
Icon(
438
imageVector = Icons.Default.BrokenImage,
439
contentDescription = "Image not available"
440
)
441
}
442
} else {
443
Image(
444
painter = painterResource(Res.drawable.user_photo),
445
contentDescription = "User photo",
446
modifier = Modifier.size(100.dp),
447
onError = { hasError = true }
448
)
449
}
450
}
451
```
452
453
### Network Failures
454
455
```kotlin { .api }
456
@OptIn(ExperimentalResourceApi::class)
457
@Composable
458
fun NetworkAwareResourceLoading() {
459
var isOnline by remember { mutableStateOf(true) }
460
461
LaunchedEffect(Unit) {
462
// Monitor network status
463
isOnline = checkNetworkStatus()
464
}
465
466
if (isOnline) {
467
Image(painterResource(Res.drawable.online_content))
468
} else {
469
// Show cached or offline content
470
Image(painterResource(Res.drawable.offline_placeholder))
471
Text("Offline mode")
472
}
473
}
474
```
475
476
## WASM-Specific Considerations
477
478
### Bundle Size Optimization
479
480
- **Image compression**: Use WebP and optimized formats
481
- **Font subsetting**: Include only required character sets
482
- **Tree shaking**: Unused resources are automatically excluded
483
- **Compression**: Resources are compressed in the build process
484
485
### Loading Performance
486
487
- **Async loading**: All resources load asynchronously
488
- **Streaming**: Large resources can be streamed
489
- **Parallel loading**: Multiple resources load simultaneously
490
- **Progressive enhancement**: App starts before all resources load
491
492
### Memory Management
493
494
- **Automatic cleanup**: Browser manages resource memory
495
- **Cache limits**: Browser enforces cache size limits
496
- **Manual cleanup**: Use `DisposableEffect` for large resources
497
498
```kotlin { .api }
499
@OptIn(ExperimentalResourceApi::class)
500
@Composable
501
fun LargeResourceManager() {
502
DisposableEffect(Unit) {
503
val largeResource = loadLargeResource()
504
505
onDispose {
506
// Cleanup if needed
507
largeResource.dispose()
508
}
509
}
510
}
511
```