A modern framework for full stack web apps in Kotlin, built upon Compose HTML
This small release fixes two regression with routes introduced in v0.17.2 -- one subtle dealing with trailing slashes, and another significant one that broke dynamic routes.
[!IMPORTANT] Planning to upgrade? Review instructions in the README.
The release also adds a handful of misc. features that are coming along for the ride.
"/{folder}/{page}"
should match "/example/route"
providing params["folder"] == "example"
and params["page"] == "route"
"/a/b/"
and the user visits "/a/b"
(no trailing slash), Kobweb will automatically suffix the "/"
for you at the end.:focus-within
psuedo-classFaThreads
for Meta's "Threads" logo.![example](/some-image.png 'This is a title')
Full Changelog: https://github.com/varabyte/kobweb/compare/v0.17.2...v0.17.3
This release adds a few features and has also migrated its dependencies to target Compose 1.6.2. (Projects using 1.6.1 should still work).
[!IMPORTANT] Planning to upgrade? Review instructions in the README.
The biggest change is support for specifying route redirects, which you can read more about here.
server:
redirects:
- from: "/socials/facebook/([^/]+)"
to: "/socials/meta/$1"
lang
setting, e.g. kobweb { app { index { lang.set("fr") } } }
in your build script
lang.set("")
if you want to clear the language setting entirely.Modifier.backdropFilter
now works on Safari targetsFull Changelog: https://github.com/varabyte/kobweb/compare/v0.17.1...v0.17.2
This is a small release which adds a new feature to Kobweb Workers and has also migrated its dependencies to target Compose 1.6.1 and Kotlin 1.9.23.
[!IMPORTANT] Planning to upgrade? Review instructions in the README.
WorkerFactory
interface has changed and will cause compile errorsIn order to support transferable objects, the WorkerFactory
interface has changed from using a lambda:
interface WorkerFactory<I, O> {
fun createStrategy(postOutput: (O) -> Unit): WorkerStrategy<I>
}
to using a new class called OutputDispatcher
:
interface WorkerFactory<I, O> {
fun createStrategy(postOutput: OutputDispatcher<O>): WorkerStrategy<I>
}
Please update any implementations accordingly. Once the signature has been updated, your code should compile again as before.
For example, here is the diff in the empty
template:
internal class EchoWorkerFactory : WorkerFactory<String, String> {
- override fun createStrategy(postOutput: (String) -> Unit) = WorkerStrategy<String> { input ->
+ override fun createStrategy(postOutput: OutputDispatcher<String>) = WorkerStrategy<String> { input ->
postOutput(input) // Add real worker logic here
}
}
We occasionally audit the codebase for deprecated code older than 6 months. In this case, we removed a lot. Hopefully, this won't affect you, if you already moved away from such code.
You can review this commit to see if a compile error you're getting was due to such a method
Full Changelog: https://github.com/varabyte/kobweb/compare/v0.17.0...v0.17.1
This release is identical to v0.16.3 but has migrated its dependencies to target Compose 1.6.0.
[!IMPORTANT] Planning to upgrade? Review instructions in the README.
:warning: :warning: :warning: If you see the androidx.collections
error :warning: :warning: :warning:
If after upgrading, you start getting this build error:
> Could not find androidx.collection:collection:1.4.0.
Searched in the following locations:
- https://repo.maven.apache.org/maven2/androidx/collection/collection/1.4.0/collection-1.4.0.pom
- https://maven.pkg.jetbrains.space/public/p/compose/dev/androidx/collection/collection/1.4.0/collection-1.4.0.pom
Required by:
project :site > org.jetbrains.compose.runtime:runtime:1.6.0 > org.jetbrains.compose.runtime:runtime-desktop:1.6.0 > org.jetbrains.compose.collection-internal:collection:1.6.0
make sure you have the google
repository added to your list of project repositories. This requirement changed in 1.6.0!
Before
// build.gradle.kts
repositories {
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
maven("https://us-central1-maven.pkg.dev/varabyte-repos/public")
}
After
// build.gradle.kts
repositories {
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
// ↓↓↓↓↓
google() // Add me!!!
// ↑↑↑↑↑
maven("https://us-central1-maven.pkg.dev/varabyte-repos/public")
}
Full Changelog: https://github.com/varabyte/kobweb/compare/v0.16.3...v0.17.0
This is a small release with some minor fixes. However, we're cutting it early in preparation for a follow-up release that will be compatible with a newer Compose version.
[!IMPORTANT] Planning to upgrade? Review instructions in the README.
Fixed an issue with Markdown code generating incorrect output when links contained inline code formatting
[see `ExampleClass` docs for more information](https://mysite.com/docs/exampleclass)
Breakpoint types refactored to be more sensible
Modifier.setWidth(Breakpoints.MD)
Added new PageContext
parameters for convenience.
https://mysite.com/a/b/c?id=123#fragment
https://mysite.com
/a/b/c?id=123#fragment
window.location
property for, but they fit nicely inside PageContext
as well for easy access.Full Changelog: https://github.com/varabyte/kobweb/compare/v0.16.2...v0.16.3
This micro release contains a fix for a critical bug introduced in the v0.16.0 route generation algorithm that may apply to a small subset of users.
You will know if this is you if Kobweb v0.16.0 generates a site that won't start up, with the dev console complaining about an invalid path that doesn't start with a slash.
[!IMPORTANT] Planning to upgrade? Review instructions in the README.
Due to the Compose / Kotlin target increase in v0.16.1, if you upgrade from v0.16.0 to this one, ensure that you also update to Compose v1.5.12 and Kotlin v1.9.22.
Who is affected? Specifically, if you use camel casing in your project's package, e.g. com.example.mySite
, then v0.16.0 will break on your site.
// site/build.gradle.kts
plugins { /* ... */ }
group = "com.example.mySite" // <-- Camel casing here breaks in v0.16.0, fixed in v0.16.2+
// ...
Our new route generation algorithm should have only been constrained to the part of the package under the pages
package, e.g. com.example.mySite.pages.exampleRoute
should only have transformed exampleRoute
and not mySite
. Once we transformed the earlier part, however, we ended up generating a weird route that broke logic further down the line. Unfortunately, we missed this in our testing, and apologize for any inconvenience.
Thanks to @TheDome0 for their quick bug report, letting us turn around on this issue quickly.
If you were not affected by this issue, updating is not required but it otherwise harmless.
Full Changelog: https://github.com/varabyte/kobweb/compare/v0.16.1...v0.16.2
This release is identical to v0.16.0 but has migrated its dependencies to target Compose 1.5.12 and Kotlin 1.9.22.
[!IMPORTANT] Planning to upgrade? Review instructions in the README.
This release changes the way Kobweb generates multi-word parts of routes (e.g. example-page
instead of examplepage
).
:warning: This provides much more standard, cleaner URLs (that are additionally more SEO friendly). This may end up being a little bit of work to some users, but a lot of care went in to make sure old sites will not break. This felt like a significant enough change that we decided to bump up the "major" version number. You can read more about legacy routes here.
This release also includes a handful of quality-of-life features and under-the-hood improvements.
[!IMPORTANT] Planning to upgrade? Review instructions in the README.
kobweb.markdown.process
callback which you can set to iterate over all markdown files in your project.
dependencies
block.
user-valid
psuedo-class
configAsKobwebWorker("workername")
, which is symmetrical to the configAsKobwebApplication("appname")
method we all know and loveWe are always grateful to community support. Thanks so much for your help!
Full Changelog: https://github.com/varabyte/kobweb/compare/v0.15.4...v0.16.0
This release introduces Kobweb Workers, a wrapper around the standard web workers technology.
[!NOTE] Web workers are a way to create an isolated script that your website can run and communicate with. Web workers run on their own thread, so it's a useful place to put computationally expensive background logic (i.e. stuff that calculates things or manages complicated state and doesn't need to interact with the UI), to avoid affecting the responsiveness and performance of your main site.
Planning to upgrade? Review instructions in the README.
Getting web workers working in vanilla Kotlin/JS can be a bit of a pain (example project here), but if you are writing a Kobweb site, it couldn't be much easier.
You just create a module that applies the Kobweb Worker Gradle plugin and then implement a single interface which provides your worker logic, and Kobweb takes care of the rest. As a bonus, you can also specify rich types for your input and output messages, which we recommend using Kotlinx Serialization to help with:
@Serializable
class CalculatePiInput(val numDigits: Int)
@Serializable
class CalculatePiOutput(val digits: List<Int>)
private fun calculatePi(numDigits: Int): List<Int> { /* ... */ }
internal class CalculatePiWorkerFactory
: WorkerFactory<CalculatePiInput, CalculatePiOutput> {
override fun createStrategy(postOutput: (CalculatePiOutput) -> Unit) =
WorkerStrategy<CalculatePiInput> { input ->
postOutput(calculatePi(input.numDigits))
}
override fun createIOSerializer() = Json.createIOSerializer()
}
and after writing that, a CalculatePiWorker
class will be automatically generated, easy to use in your Kobweb site:
val worker = rememberWorker {
CalculatePiWorker { output -> println("Pi is 3.${output.digits.joinToString("")}") }
}
// Later, say in response to some input being entered or a button being pressed
worker.postInput(31415)
Please check out the official documentation for more details.
compose-html-ext
IntersectionObserver.
See notes section for more information.accent-color
Modifierappearance
Modifiercaption-side
Modifierclear
Modifiercolumn-rule
Modifierfont
Modifier
Modifier.font { style(...); size(...) }
borderRadius
Modifier
Modifier.borderRadius { topLeft(5.px) }
border
ModifiercssRule
API to start with a space for Descendant selectors
cssRule(" h1")
, after: cssRule("h1")
cssRule(">h1")
, but in my opinion, the code reads fine without it.ApiContext
(the context value passed into @Api
methods) improvements
compose-html-ext
non-Compose bits refactored into browser-ext
We deprecated quite a bit of code as part of a code migration in this release. You may see a warning with this latest version of Kobweb, something that looks like:
We are migrating non-Compose utilities to a new artifact. Please change your imports to use `com.varabyte.kobweb.browser.example.stuff` instead (that is, `compose` → `browser`).
Unfortunately, the sort of migration we're doing here is not one that we could easily provide instructions to the IDE so it could automatically replace old code with new. You'd end up with code like:
import com.varabyte.kobweb.browser.example.stuff
import com.varabyte.kobweb.compose.example.stuff
which replaces your easy to understand deprecation warning with an opaque "ambiguity" warning.
If you run into this, the best recommendation I can give you is to manually change the import statement, changing the "compose" part to "browser". For example, com.varabyte.kobweb.
to compose.example.stuffcom.varabyte.kobweb.browser.example.stuff
.
When we first started working on Kobweb, we needed a place to put utility methods and classes that didn't exist in Compose HTML but wish they did. So we created an artifact that even users who did not use Kobweb could pull into their Compose HTML projects and benefit from.
But we recently realized we could have gone further -- some of these utilities don't depend on Compose! But apply to Kotlin/JS browser projects more generally.
Especially with the introduction of workers (which are modules that don't depend on Compose), we decided to do the migration now.
topAndBottom
to topBottom
, etc.As we get closer to 1.0, we're reviewing some of our old code and trying to come up with consistent rules. We made the decision that "and" should not be included when it's obvious from context.
So we decided that some of our original naming was a mistake. This can cause compile errors in projects which reference parameter names like topAndBottom
or leftAndRight
directly. If that happens, please rename them.
As part of the browser-ext
migration discussed above, a minor scar was that the IntersectionObserver
class obviously belonged in the non-Compose part, but the API provided by compose-html-ext
did use a nice, rich CSSMargin
type that ultimately requires Compose HTML.
Therefore, we moved the class into browser-ext
but added a new extension constructor in compose-html-ext
. If you happened to be using this constructor, you'll get a compile error. You can fix this by importing the extension constructor (which is implemented as an invoke
operator on the Option
class).
import com.varabyte.kobweb.browser.dom.observers.IntersectionObserver
import com.varabyte.kobweb.compose.dom.observers.invoke // !!! Add this!
// Later
IntersectionObserver(
IntersectionObserver.Options(
rootMargin = margin(leftRight = 10.px) // to allow using `margin`
)
)
You could of course consider doing the following, which sidesteps importing "invoke":
import com.varabyte.kobweb.browser.dom.observers.IntersectionObserver
// Later
IntersectionObserver(
IntersectionObserver.Options(
rootMargin = margin(leftRight = 10.px).toString()
)
)
We had some new contributors in this release.
Thanks so much for taking your time to help Kobweb be a better project!!
And perhaps more than usual, thanks to @DennisTsar for helping me navigate myself out of countless dead ends for poor approaches I took with the Worker feature.
Full Changelog: https://github.com/varabyte/kobweb/compare/v0.15.3...v0.15.4
This release brings some extra functionality to Kobweb's export feature.
Planning to upgrade? Review instructions in the README.
export
block has moved from kobweb.export
to kobweb.app.export
, a long overdue migration.kobweb.app.export.filter
to filter some routes out of being exported, which can save a lot of time in case you have some routes that don't need to be cached for SEO purposes.kobweb.app.export.enableTraces
to collect snapshots of your pages' progress which you can use to visualize what was happening when they were being visited at export time.kobweb.app.export.timeout
property, for example timeout.set(1.minute)
.
ApiContext
and InitApiContext
classes now expose a property which tells you if your server is running in dev or prod mode; for example, ctx.env.isDev
.
size(width, height)
, minSize(width, height)
, and maxSize(width, height)
modifiers.CSS*NumericValue
types for all relevant CSS units.
CSSLengthValue
, CSSPercentageValue
, etc.. Kobweb now provides numeric versions, e.g. CSSLengthNumericValue
, CSSPercentageNumericValue
, etc.export
block has movedThe export feature is about as old as features in this framework get. As a result, it's no surprise that the export
block location is outdated!
The new location lives under the app
block (since only application modules should be influencing it):
// site/build.gradle.kts
// Old location
kobweb {
export {
includeSourceMap.set(false)
}
}
// New location
kobweb {
app {
export {
includeSourceMap.set(false)
}
}
}
It's trivial to move, but Gradle / IntelliJ unfortunately doesn't surface deprecation warnings directly (i.e. it would have been nice if the export
name was crossed out in the IDE if in the old location), so instead you will get a warning at export time.
Here's a quick example where we filter out exporting all admin pages from our site, since those will always require auth anyway and shouldn't be cached for SEO purposes. The filter
scope includes a route
property you can use to test the current route being exported:
// site/build.gradle.kts
kobweb {
app {
export {
filter { !route.startsWith("/admin/") }
}
}
}
Let's say you have a page on your site that is taking 10 seconds to export, when all your other pages only take 1. What is happening?
Thanks to a feature provided by Playwright (the framework by Microsoft we use for taking browser snapshots), enabling traces is easy:
// site/build.gradle.kts
kobweb {
app {
export {
enableTraces()
}
}
}
You can look at the docs for enableTraces
to see how to configure it, but by default, when traces are enabled, Kobweb will drop trace.zip
files under your .kobweb/export-traces
folder, one per page exported.
You can then drag and drop those files into the page at https://playwright.dev/docs/trace-viewer to investigate its details.
Between trace snapshots and server logs, you should be able to diagnose what's going on.
Full Changelog: https://github.com/varabyte/kobweb/compare/v0.15.2...v0.15.3