fritz2 Build reactive web-apps in pure Kotlin
fritz2 is a lightweight, typesafe, data-driven library for building reactive web-apps in pure Kotlin, heavily depending on coroutines and flows.
FEATURES
Why you should use fritz2
Pure Kotlin
fritz2 makes heavy use of Kotlin standard features (especially flows) and has no external dependencies. Based upon Kotlin's outstanding capabilities to build DSLs, fritz2 offers a nice declarative and typesafe syntax to structure your UI code in a comfortable and easy to read way.
Precise Data-Binding
fritz2 offers precise data binding for your ui-elements. This means that when parts of your data model change, exactly those and only those DOM-nodes depending on the changed parts will automatically change as well - no virtual dom needed.
Easy to Learn
fritz2 is built around just a few basic concepts (Stores, Handlers, Tags, etc.). You can easily learn how to use them from our documentation or the examples we provide. Enjoy!
App-Scaling
fritz2 allows you to quickly build small apps with a few lines of code, as well as enterprise scale applications with a focus on reusable components, clean code, and structure. The functional reactive concept makes the resulting code easy to read and to maintain.
Everything you need
fritz2 includes everything you need to start your project: state management even for complex nested models, validation, routing, REST, WebSockets, and WebComponents as well as a set of headless components.
Reusability
fritz2 uses Kotlin's multiplatform-abilities, so you'll write your data classes and validation code just once and use it for both client and server. Use your favorite tool-chain (Gradle, IDEA, etc.) to jump right in and build your apps.
SHOW ME CODE
How your web-app looks in fritz2
Let's start with creating our first elements, or tags as we call them, using the DSL.
First we need to call the render
function in order to create a RenderContext
for our tags. Every tag is just a function representing an HTML-element, <div>
in this case.
You can nest them just like in HTML. To style your tags, add CSS-class-names directly as first parameter.
fun main() {
render {
div("my-style-class") {
h2 {
+"Hello Peter!"
}
}
}
}
To divide your code into reusable fragments, simply write functions with a RenderContext
receiver
- they can be called from inside any render-context.
In this example we create a greet
function and call it multiple times with different name
s.
fun RenderContext.greet(name: String) {
h2 {
+"Hello $name!"
}
}
render {
div("my-style-class") {
greet("Peter")
greet("Paul")
greet("Marry")
}
}
Let's get a little more reactive by using a Store
to store our data.
Stores are needed for two-way data binding in fritz2.
Access the store's data as a Flow
by calling store.data
,
then render it into the value
attribute of the <input>
tag (this is one-way data binding).
To update the data inside the store, call the update
handler by using the
handledBy
function (two-way data binding).
render {
val store = storeOf("Hello Peter")
div("...") {
input("...") {
type("text")
value(store.data)
changes.values() handledBy store.update
}
p("...") {
store.data.renderText(into = this)
}
}
}
As you have seen, fritz2 considers data-handling a first class citizen of any reactive web-app. Think of the UI as a rule driven, derived representation of the application data. That's why our framework also natively supports validation as part of the data-handling.
In order to evaluate data correctness, create a container for the messages by implementing
ValidationMessage
. You also have to provide a Validation
instance by using the factory
function validation
. The latter contains all the domain rules to check the validity state.
There is a special storeOf
factory function which directly accepts a
Validation
-object. It will be automatically evaluated on each update. Last but not least,
use the provided store.messages
flow to display errors and warnings for the user.
class Message(
override val path: String,
val text: String
): ValidationMessage {
override val isError: Boolean = true
}
val mailRegex = Regex("""\S+@\S+\.\S+""")
val validation = validation<String, Message> {
if(!mailRegex.matches(it.data))
add(Message(it.path,"Not a valid mail address"))
}
render {
val store = storeOf("", validation)
div("...") {
input("...") {
type("text")
placeholder("Enter e-mail address")
value(store.data)
changes.values() handledBy store.update
}
store.messages.renderEach {
p("...") {
+it.text
}
}
}
}
Latest Blog Article
What we have learned about reuse and components in web front-end development.
This article is about how we built and delivered components with fritz2 so far, which problems we noticed more and more and why a new approach is the solution for us. At the same time we say goodbye to the components delivered with fritz2 up to verson 0.14 and introduce the new and imho better replacement for them.