Redux KMP 🔄
A Kotlin Multiplatform Redux library with modern Redux Toolkit features, type-safe DSLs, and Compose Multiplatform integration.

✨ Features
📱 Supported Platforms
🚀 Quick Start
1. Add Dependency
dependencies {
implementation("in.sitharaj:redux-kmp:1.0.0")
}
2. Define State & Actions
data class CounterState(
val count: Int = 0,
val loading: Boolean = false
) : State
: {
Increment : CounterAction
Decrement : CounterAction
( value: ) : CounterAction
}
3. Create Reducer
val counterReducer = reducer<CounterState> {
on<CounterAction.Increment> { state, _ ->
state.copy(count = state.count + 1)
}
on<CounterAction.Decrement> { state, _ ->
state.copy(count = state.count - 1)
}
on<CounterAction.SetCount> { state, action ->
state.copy(count = action.value)
}
}
4. Create Store
val store = createStore(
initialState = CounterState(),
reducer = counterReducer,
scope = CoroutineScope(Dispatchers.Main)
) {
addMiddleware(ThunkMiddleware())
addMiddleware(LoggingMiddleware(tag = "Counter"))
}
5. Dispatch Actions
store.dispatch(CounterAction.Increment)
store.state.collect { state ->
println("Count: ${state.count}")
}
6. Use with Compose
@Composable
fun CounterScreen() {
val state by store.state.collectAsState()
Column {
Text("Count: ${state.count}")
Button(onClick = { store.dispatch(CounterAction.Increment) }) {
Text("Increment")
}
}
}
🛠 Redux Toolkit Features
Memoized Selectors
val selectCount = selector<AppState, Int> { it.counter }
val selectDoubleCount = createSelector(selectCount) { count ->
count * 2
}
val selectTotal = createSelector(
{ state: CartState -> state.items },
{ state: CartState -> state.taxRate }
) { items, taxRate ->
items.sumOf { it.price } * (1 + taxRate)
}
Async Thunk
val fetchUser = createAsyncThunk<String, User, AppState>(
typePrefix = "users/fetchById"
) { userId, thunkApi ->
api.getUser(userId)
}
val userReducer = reducer<UserState> {
on<AsyncThunkPending<String>> { state, _ ->
state.copy(loading = true)
}
on<AsyncThunkFulfilled<String, User>> { state, action ->
state.copy(loading = false, user = action.payload)
}
on<AsyncThunkRejected<String>> { state, action ->
state.copy(loading = false, error = action.error.message)
}
}
Entity Adapter
val usersAdapter = createEntityAdapter<User> { it.id }
data class UsersState(
val users: EntityState<User> = usersAdapter.getInitialState()
) : State
usersAdapter.addOne(state.users, newUser)
usersAdapter.updateOne(state.users, id) { it.copy(name = "New") }
usersAdapter.removeOne(state.users, id)
val allUsers = usersAdapter.selectAll(state.users)
val user = usersAdapter.selectById(state.users, "123")
Listener Middleware
val listenerMiddleware = createListenerMiddleware<AppState>()
listenerMiddleware.addListener<UserAction.Login> { action, api ->
api.dispatch(AnalyticsAction.Track("login"))
api.fork {
delay(1000)
api.dispatch(NotificationAction.Show("Welcome!"))
}
}
Slice
val counterSlice = createSlice<CounterState>(
name = "counter",
initialState = CounterState()
) {
reduce("increment") { state, _ ->
state.copy(count = state.count + 1)
}
reduce<Int>("addAmount") { state, payload ->
state.copy(count = state.count + payload)
}
}
val reducer = counterSlice.reducer
store.dispatch(counterSlice.actions.invoke("increment"))
📁 Library Structure
redux-kmp/src/commonMain/kotlin/in/sitharaj/reduxkmp/
├── core/
│ ├── Action.kt
│ ├── State.kt
│ ├── Reducer.kt
│ ├── ReducerDSL.kt
│ ├── Store.kt
│ ├── StoreDSL.kt
│ └── Selector.kt
├── middleware/
│ ├── Middleware.kt
│ ├── ThunkMiddleware.kt
│ ├── LoggingMiddleware.kt
│ └── ListenerMiddleware.kt
├── toolkit/
│ ├── AsyncThunk.kt
│ ├── EntityAdapter.kt
│ └── Slice.kt
├── compose/
│ └── ComposeIntegration.kt
└── ReduxKmp.kt
📦 Sample App
The sample app demonstrates all Redux features in a Chat Application:
- EntityAdapter for normalized messages/users
- Selectors for derived state (unread count, typing users)
- AsyncThunk for sending messages
- ListenerMiddleware for auto-reply simulation
./gradlew :sample:run
./gradlew :androidApp:installDebug
./gradlew :sample:jsBrowserDevelopmentRun
📚 Documentation
- Documentation Site - Full API docs and tutorials
- GPG Setup Guide - Maven Central signing
cd docs-site
npm install
npm run dev
🧪 Testing
./gradlew :redux-kmp:check
./gradlew :redux-kmp:desktopTest
./gradlew :redux-kmp:jsTest
./gradlew :redux-kmp:iosSimulatorArm64Test
📤 Publishing
./gradlew :redux-kmp:publishToMavenLocal
./gradlew :redux-kmp:zipBundle
📄 License
Copyright 2024 Sitharaj Seenivasan
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http:
☕ Support
🔗 Links