Using Chart.js in Kobweb :thread:
# kobweb
d
Using Chart.js in Kobweb 🧵
I think it will help if you can share the ideal Kotlin code you want to write.
r
Sure.
d
(It will help identify what subset of the APIs you want to bind to)
r
I'd like to be able to call a composable function like this:
Copy code
Chart(
   /* chart data */
)
Yep. I'm just trying to go through the example.
Presently, I have this in my build.gradle.kts:
Copy code
kobweb {
    app {
        index {
            description.set("Wagemix AI")

            head.add {
                val chartjs = routePrefix.prependTo("/chart.js")
                script { src = "$chartjs/dist/chart.js" }
            }
        }
    }
}
/* ... */
kotlin {
    sourceSets {
        jsMain.dependencies {
            implementation(npm("chart.js", version = "4.4.9"))
        }
    }
}
And I've symlinked the node module:
Copy code
ryan[wagemix] (main)$ readlink site/src/jsMain/resources/public/chart.js
../../../../../build/js/node_modules/chart.js/
I've created the binding like this:
Copy code
package bindings.chartjs

import org.w3c.dom.Element
import kotlin.js.Json

external class Chart(element: Element, options: Json)
d
In your network tools, verify that chart.js is getting pulled down and not 404'ing
r
Screenshot 2025-04-21 at 5.18.14 AM.png
👍 1
There must be something I'm not understanding about the external modifier.
Take a look at that?
r
Oh.
That's actually more detailed but very close to what I wanted.
If this is published, then I'll just use it.
d
Someone linked to that in the Discord a while back
r
Ah--I never check Discord.
d
You can try using it, although note that it hasn't been touched for a year
r
If nothing else, it's a better starting point than what I had.
d
so it using it doesn't work then you can always copy/paste just what you need
My guess is you weren't supposed to use
Element
but instead
HtmlElementCanvas
but not sure
r
That jumped out at me, too. Testing now.
d
https://www.chartjs.org/docs/latest/api/#chartitem seems to be the union of types the first parameter accepts
r
Okay--getting a little closer:
Copy code
@file:JsModule("chart.js")
@file:JsNonModule

package bindings.chartjs

import org.w3c.dom.HTMLCanvasElement
import kotlin.js.Json

external class Chart(element: HTMLCanvasElement, options: Json) {
    fun destroy()
}
Seems to allow the
Chart
class to be found.
🙌 1
d
Once you have a hook in place it tends to come together pretty quickly from that point
The hardest part is getting over the informationless exceptions
r
It's working.
d
Nice!!
r
You're right that this is a big pain.
d
There are attempts to automate it but it's a very hard problem because Kotlin and JS are puzzle pieces that don't really fit together very well. You end up having to resort to
dynamic
a lot.
r
Here's the process that I'm finding is working for me: 1. common: Create interfaces that enable you to create convenient objects in common Kotlin code. These are the interfaces that you work with. For these classes, focus on ease of use. This gives you a variety of ways to deal with union types--which I've found
sealed class
to work quite well. 2. js: Create external interfaces that map directly to the JS objects. Here is where you set anything that is a union type to
dynamic
. Here also, you're going to want to use
var
properties instead of
val
unless you know you're working specifically with immutable data inside of Javascript. 3. js: Create a function that map your common interfaces into the JS extern interfaces so that you can create the JS objects from the common code. Use that really convenient builder function from https://github.com/hfhbd/chartJS-compose/blob/main/src/main/kotlin/index.module_chart.js.kt 4. If necessary, map things back the other way (in my case, I don't have to do this) It's laborious, but especially for kobweb, it can be SUPER useful to have compatibility with these really nice javascript libraries.
d
100%. That's the approach I took when I was experimenting wrapping Firebase in Kotlin (note: this was never a project I intended to complete, it was just a proof of concept learning tool for me): https://github.com/bitspittle/firebase-kotlin-bindings Raw JS bindings layer and public API that is Kotlin-idiomatic.
Note that this results in a really funny API signature:
fun internal external