Kobweb Versions Save

A modern framework for full stack web apps in Kotlin, built upon Compose HTML

v0.17.3

4 weeks ago

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.

Changes

Frontend

  • Fixed regression where dynamic routes stopped capturing values correctly
    • For example, the dynamic route "/{folder}/{page}" should match "/example/route" providing params["folder"] == "example" and params["page"] == "route"
  • Fixed regression where missing slashes wouldn't get auto-appended.
    • For example, if you've registered the route "/a/b/" and the user visits "/a/b" (no trailing slash), Kobweb will automatically suffix the "/" for you at the end.
  • Added tests for route logic so that we don't break subtle route features again...
  • Added :focus-within psuedo-class
  • Updated FA icons to v6.5.2, which adds about a dozen or two new icons, including FaThreads for Meta's "Threads" logo.

Markdown

  • Added support for surfacing the optional title of an image link, e.g. ![example](/some-image.png 'This is a title')

Full Changelog: https://github.com/varabyte/kobweb/compare/v0.17.2...v0.17.3

v0.17.2

1 month ago

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"

Changes

Frontend

  • Navigation logic updated to take redirects into account.
  • Users can now configure their site's lang setting, e.g. kobweb { app { index { lang.set("fr") } } } in your build script
    • If not set, defaults to "en"
    • You can use lang.set("") if you want to clear the language setting entirely.

Silk

  • Fixed an issue with the checkbox implementation that could occasionally mess up scrolling vertical layouts.
  • Modifier.backdropFilter now works on Safari targets

Backend

  • Added routing support for redirects. Visiting an old URL will now return a 301 redirect code.

Full Changelog: https://github.com/varabyte/kobweb/compare/v0.17.1...v0.17.2

v0.17.1

1 month ago

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.


Changes

Workers

  • Kobweb workers now support transferable objects
    • Transferable objects are a web solution to passing very large objects between an application and its workers
    • You can read more about them in the README
    • :warning: Backwards incompatible Unfortunately, this change will cause a compile error for anyone who is already using workers. The fix is easy, however. See the Notes section for more information.

Notes

The WorkerFactory interface has changed and will cause compile errors

In 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
   }
}

Deprecated code

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

v0.17.0

2 months ago

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

v0.16.3

2 months ago

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

    • For example, this used to break:
      [see `ExampleClass` docs for more information](https://mysite.com/docs/exampleclass)
      
  • Breakpoint types refactored to be more sensible

    • so you can now do e.g. Modifier.setWidth(Breakpoints.MD)
  • Added new PageContext parameters for convenience.

    • For a URL https://mysite.com/a/b/c?id=123#fragment
      • ctx.origin: https://mysite.com
      • ctx.pathQueryAndFragment: /a/b/c?id=123#fragment
    • These were values you could query JS's 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

v0.16.2

3 months ago

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

v0.16.1

3 months ago

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.

v0.16.0

3 months ago

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.


Markdown

  • Introduced the new kobweb.markdown.process callback which you can set to iterate over all markdown files in your project.
    • This feature is very useful, for example, if you want to create a listing file that enumerates all your other markdown posts.
    • Please see the added README section for more information.

Backend

  • You can now add custom headers into your response.

Frontend

  • :rotating_light: Camel-case packages and filenames now generate hyphenated routes!
    • This is recommended for SEO, and many users have been using route overrides to do this. Hopefully the defaults are a lot more sensible now.
    • You can review Page and Package docs for the latest information.
    • Kobweb will still work with old sites, as for now it will intercept links to legacy style routes and handle them automatically. However....
    • You should still consider auditing your site and disabling these legacy route redirects. The README provides a simple list of steps to follow to do this.
  • We now support declaring Kobweb Server Plugin dependencies in a more Gradle-idiomatic way, using the dependencies block.
  • Added support for the user-valid psuedo-class
  • Updated the kotlinx.html dependency to its recently released 0.11.0 version

Gradle

  • Tons of cleanup and tweaking which should make the Kobweb Gradle Plugins play more friendly with things like the Gradle configuration cache. Hopefully this will slightly improve compile times.

KSP

  • Fixed our KSP logic so it now properly implements multi-round processing.
    • This could matter to you if are writing your own KSP processors that generate Kobweb code.

Worker

  • Changed the way to specify a worker name in a Kobweb worker, which is now more consistent with Kobweb applications.
    • configAsKobwebWorker("workername"), which is symmetrical to the configAsKobwebApplication("appname") method we all know and love
  • If a worker fails to serialize / deserialize its input / output arguments, the dev console now prints out a useful warning message instead of swallowing it quietly.

Thanks to our contributors!! :tada: :tada: :tada:

  • @kjeller took on the markdown feature discussed in these release notes. They also helped clean up some badly indented formatting in generated font code.
  • @remblej provided the feedback and the initial code draft for custom headers in backend responses.

We 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

v0.15.4

4 months ago

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.


Frontend

  • Kobweb Worker support :tada: :tada: :tada:
  • Refactor non-Compose utility methods and classes out of compose-html-ext
    • :warning: This could cause a bunch of compile warnings in your project. See notes section for more information.
    • :warning: This might cause an error if you are using IntersectionObserver. See notes section for more information.
  • Removed "And" from some parameter names for consistency and simplicity.
    • :warning: Renamed parameters might cause an error in your project. See notes section for more information.
  • A bunch of Modifier additions and improvements
    • New accent-color Modifier
    • New appearance Modifier
    • New caption-side Modifier
    • New clear Modifier
    • New column-rule Modifier
    • New top-level font Modifier
      • Font modifiers have existed for a long time, but now you can do this: Modifier.font { style(...); size(...) }
    • New top-level borderRadius Modifier
      • Makes it easy to set just the corners you need, e.g. Modifier.borderRadius { topLeft(5.px) }
      • Additional support to make it easy to set elliptical border radii
    • More methods for configuring color and style added to the top-level border Modifier
  • Removed the requirement for the cssRule API to start with a space for Descendant selectors
    • In other words, before: cssRule(" h1"), after: cssRule("h1")
    • You can leave the space in if you'd like, e.g. for symmetry with cssRule(">h1"), but in my opinion, the code reads fine without it.

Backend

  • ApiContext (the context value passed into @Api methods) improvements
    • Now includes all headers sent with the request.
    • Added a bunch of connection details to the request (so you can know where the request came from, for example)

Gradle

  • Fixed an issue with my logic that could sometimes cause Gradle to issue you a spurious demand to add an explicit dependency on ":kobwebGenSiteIndexTask"

Notes

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.compose.example.stuff to com.varabyte.kobweb.browser.example.stuff.

Rationale for this migration

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.

IntersectionObserver API changes

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()
   )
)

Thanks!

We had some new contributors in this release.

  • @ellet0 helped us fill out some of the CSS gaps in our APIs (most of the new modifiers in this release)! They also improved site process by adding a new PR testing workflow. I really appreciate their interest in Kobweb -- it's clear they have a lot of ideas for improvements.
  • @dieter115 suggested APIs for improving our file loading utilities, allowing loading multiple files at once.

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

v0.15.3

5 months ago

This release brings some extra functionality to Kobweb's export feature.

Planning to upgrade? Review instructions in the README.


Export

  • The export block has moved from kobweb.export to kobweb.app.export, a long overdue migration.
  • You can now use 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.
  • You can now use 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.
  • You can configure the timeout used when exporting by setting the kobweb.app.export.timeout property, for example timeout.set(1.minute).
    • The default timeout is 30 seconds.

Backend

  • The 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.
    • You can use this information to decide if you should create development versions of services, like a database populated with fake data.

Frontend

  • Added convenience size(width, height), minSize(width, height), and maxSize(width, height) modifiers.
  • Added new CSS*NumericValue types for all relevant CSS units.
    • Compose HTML provides non-Numeric versions, e.g. CSSLengthValue, CSSPercentageValue, etc.. Kobweb now provides numeric versions, e.g. CSSLengthNumericValue, CSSPercentageNumericValue, etc.
    • Updated all Kobweb APIs to use these new types.
    • Read more about this change in the README.

Notes

The export block has moved

The 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.

Filtering routes

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/") }
      }
   }
}

Enabling export traces

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