Getting Started with Compose for Web
Tags: kotlin, compose, multiplatform, webdev
Published: 2023-12-01 21:34:32

If you are a Kotlin developer like me, you are probably very excited about Jetpack Compose. It’s a modern UI toolkit for developing apps, and with the great work from JetBrains, it’s becoming a really interesting solution.

As a mobile developer, I’ve never gone in the weeds on how to properly develop sites with HTML, CSS, and JS. My closest experience was with React Native back in 2018. But now, with Compose, using a Kotlin DSL, I feel comfortable on doing this.

## Compose Runtime and Component Library

Before creating a project and start coding, it's important to have a small introduction to Compose runtimes. As of now, Compose has two different runtimes on the web:

1. JS + HTML
2. Wasm + Canvas

They both use the Compose Runtime underneath, but they use different components. Long story short: 

* The HTML version will have HTML composable components (like H1, P, Span, Input, Div, etc.) 
* The Canvas version will have similar components as the Android version as it uses SKIA to draw on the Canvas.

Both of them are experimental. Note that choosing how you want to build your application will impact on possible code-sharing with native if this is what you want.

For this series of posts, I'll use the HTML version and I will be able to explore more the web components.

## Creating the project

Now that we know what runtime/components we want to use, we need to create a project. My first idea was to use the [KMP project wizard](https://kmp.jetbrains.com), but unfortunately, the Web target is not available as an option for now. As a workaround, I had to create a new clean project using IntelliJ IDEA and add the dependencies manually. 

Using a clean template you will reduce the amount of code that needs to be removed/updated to make the project work. But if you prefer to use any other IDE/Template, feel free to do so and share your experience with us later :)

![IntelliJ IDEA Project Wizard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eya3ykaxbvtitksa51ff.png)

With that, the configuration is completed with a few extra lines:

1. Change the Kotlin plugin from JVM to MULTIPLATFORM
2. Add the Compose plugin
3. Declare JS source-sets
4. Import the dependencies
5. Create an HTML file

```kotlin
plugins {
    kotlin("multiplatform") version "1.9.20" // Make this multiplatform
    id("org.jetbrains.compose") version "1.5.10" // Add compose plugin
}

kotlin {
    js(IR) { // Declare JS source-sets
        browser()
        binaries.executable()
    }
    sourceSets {
        val jsMain by getting {
            dependencies {
								// Import libraries
                implementation(compose.html.core)
                implementation(compose.runtime)
            }
        }
        val jsTest by getting {
            dependencies {
                implementation(kotlin("test-js"))
            }
        }
    }
}
```

If you have a trained eye for Gradle configuration, you probably already noticed that we are importing the Compose HTML library. And that's why we need to create an HTML page that will load and be filled by our Composable code. This file should be located at `jsMain/resources/index.html`.

```html
<!DOCTYPE html>
<html lang="en">
	<head>
	    <meta charset="UTF-8">
	    <title>Compose Web</title>
	</head>
	<body>
		<div id="root"></div>
		<script src="compose-sweeper.js"></script>
	</body>
</html>
```

This can be very simple, just load the JS built from your Kotlin code and create a DOM element that will be you compose root.

## Running the project

As with any other project using Gradle as the build system, you will run most of your changes with the `gradlew` binary (or the `gradlew.bat` if you are running on Windows). Even though there are multiple commands listed when you call `gradlew tasks`, the important ones are three:

1. `./gradlew jsBrowserDevelopmentRun` - Start development server;
    1. This call supports the `--continuous` build flag that can be used for reloading the app when the code changes;
2. `./gradlew jsBrowserProductionWebpack` - Generate the bundle version of your project. 
    1. It will spit out a static site at the `build/dist/js/productionExecutable` dir;
3. `./gradlew allTests` - Run all unit tests you have implemented for your project;

## How does this work?

When you start the development server, it will compile your Kotlin code and run the `main()` function. On this function, you can call the `renderComposable` method to start a composable block inside a window element.


```kotlin
fun main() {
    renderComposable(rootElementId = "root") {
        Text("Hello World")
    }
}
```

As you can see, it will render all the content inside one root element we've defined on previous sections.


Lastly, as you can see at the end of the body block, we are importing the JS file created while building our application. The default name is just the project name with a `.js` extension.

Now that we have everything in place, we can finally run our server and have the following result:

![Compose Hello Wold](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ev7qpzkze4g7w0yr7vn6.png)

## Final Thoughts

In this first article, we are just exploring how to create and set up your project. But we can already say that Compose Web is a promising alternative for Kotlin developers who don’t want to learn HTML/CSS/JS (even though you will need it). 

In future posts, we will dig into more topics, like how to use HTML Components, Styling with CSS, Handling, and Listening to DOM events. After all the articles, you will be able to fully understand how I’ve built [this Minesweeper game](https://faogustavo.github.io/compose-sweeper/) only using Compose.