Kotlin 2.5.0 opens experimental companion blocks
JetBrains has published preliminary guidance on companion blocks and companion extensions, two experimental Kotlin 2.5.0 features that change how class-level members are declared and compiled.

JetBrains has outlined two experimental additions to Kotlin's companions mechanism, companion blocks and companion extensions, which change how the class-level members that constants, utilities and factory methods live in are declared and compiled.
Companion blocks replace the object
Those members have historically been defined in companion objects. From Kotlin 2.5.0 developers can instead write a companion block directly in the class body — syntactically close to what came before, but with a very different compilation strategy behind it. The syntax is a pair of braces rather than a nested singleton, so a vector type can carry its own zero constant without the object wrapper.
Companion extensions reach code you do not own
Companion extensions go further and declare new class members on a type, whether or not the developer controls that type and whether or not it already has a companion object. That covers Kotlin and Java classes alike, which is the interoperability gap the existing mechanism could not close: you can now hang a factory function off a class you are not allowed to edit.
Static members and multiplatform
On the JVM, companion blocks compile as static members rather than as a nested class, and that is what makes the feature interesting for Kotlin Multiplatform. Expected companion block members can now be actualised by a Java class that contains static members, which removes a long-standing friction point for shared code. JetBrains points developers at the corresponding KEEP proposal for the full design.
Opt-in flags and pre-release binaries
Both features are experimental in Kotlin 2.5.0. Companion blocks need the -Xcompanion-blocks compiler flag, while enabling blocks and extensions together needs -Xcompanion-blocks-and-extensions. The second flag makes the compiler produce pre-release binaries, so a library that uses companion extensions cannot be consumed as a dependency until the feature becomes stable. Exposing companion blocks carries no such restriction, although anyone consuming those blocks still has to switch the feature on.
Companion objects are staying
JetBrains is not deprecating companion objects, and support for them is guaranteed. There is no migration to perform, and a few cases still require an object — a companion that has to implement a particular interface, for instance, because a companion object compiles to a full class. For most other usages the team expects companion blocks to be the better fit, and deleting the object keyword is usually enough, provided the project and anything depending on it is recompiled. Tooling elsewhere in the ecosystem may not be ready yet.
Our opinion
Kotlin's companion object has always been the language's quietest oddity: a singleton class nobody names, holding the members that other JVM languages would simply have made static. Companion blocks are the admission that the JVM answer was going to win, and the telling detail is the compilation target rather than the syntax. Static members mean two-way Java interop, which is exactly why multiplatform stands to gain the most.
The cost is that adoption will be lopsided. A flag that emits pre-release binaries cannot be used by libraries that need to ship, so the feature will stall precisely where the pain is worst — shared dependencies — until it graduates. JetBrains has run this play before with multiplatform, and the honest reading of “preliminary guidance” is that the syntax is the easy part.