
Standards-compliant EPUB rendering with discrete pagination, CFI position restoration, annotations/highlights, in-book search, multiple themes, headless engine or full reader UI, and pluggable persistence.
foliate-kmp is a Kotlin Multiplatform (Compose Multiplatform) EPUB reader library. It uses foliate-js as its engine. It gives you standards-compliant EPUB rendering with discrete pagination, CFI navigation, annotations, search and themes on Android and iOS.
Stability: beta. The public API can change in a minor release until version 1.0. Each change appears in CHANGELOG.md.
| Sample Library | Reading Experience | Typography & Themes | Table of Contents |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
| Artifact | Description |
|---|---|
io.github.asadullah012:foliate-kmp-core |
Headless engine, WebView platform bridges (Android WebViewAssetLoader, iOS WKURLSchemeHandler), EpubReaderController, data models and storage interfaces. |
io.github.asadullah012:foliate-kmp-compose |
Complete Material 3 reading screen with a top navigation bar, a bottom progress scrubber, a TOC drawer, an appearance sheet, an annotations dialog, a footnote sheet and in-book search. |
| Target | Supported | Note |
|---|---|---|
| Android | Yes |
minSdk 26, compileSdk 37 |
iOS device (iosArm64) |
Yes | |
iOS simulator, Apple silicon (iosSimulatorArm64) |
Yes | |
iOS simulator, Intel (iosX64) |
No | Compose Multiplatform publishes no iosX64 artifacts. |
| Desktop, JVM, web | No |
Add the library to your build.gradle.kts:
// Option 1: the complete reader screen (this includes the core)
commonMain.dependencies {
implementation("io.github.asadullah012:foliate-kmp-compose:0.1.0-beta01")
}
// Option 2: the headless engine only, for your own UI
commonMain.dependencies {
implementation("io.github.asadullah012:foliate-kmp-core:0.1.0-beta01")
}You need no extra configuration. foliate-kmp-core carries its own consumer rules
inside the AAR.
import androidx.compose.runtime.Composable
import io.github.asadullah012.foliate.compose.ui.ReaderScreen
@Composable
fun MyBookScreen(epubFilePath: String, onBack: () -> Unit) {
ReaderScreen(
filePath = epubFilePath,
bookTitle = "Pride and Prejudice",
onNavigateBack = onBack
)
}Use this to build your own reader UI, toolbars and gesture controls:
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.fillMaxSize
import io.github.asadullah012.foliate.EpubReaderController
import io.github.asadullah012.foliate.model.EpubReaderConfig
import io.github.asadullah012.foliate.model.EpubReaderTheme
import io.github.asadullah012.foliate.ui.FoliateReaderView
@Composable
fun CustomReader(bookPath: String) {
val controller = remember { EpubReaderController() }
FoliateReaderView(
bookPath = bookPath,
controller = controller,
config = EpubReaderConfig(theme = EpubReaderTheme.SEPIA, fontSize = 20),
modifier = Modifier.fillMaxSize()
)
}The repository includes a runnable Compose Multiplatform sample app in the :sample module. It bundles a public-domain copy of Alice's Adventures in Wonderland to demonstrate pagination, themes, CFI restoration, search, and text annotations out of the box.
./gradlew :sample:installDebugOpen the Xcode project in Xcode and click Run:
open sample/iosApp/iosApp.xcodeprojOr build the framework for the Apple Silicon simulator:
./gradlew :sample:compileKotlinIosSimulatorArm64EpubReaderStorage to save progress, bookmarks
and highlights to Room, SQLite, DataStore or a remote database. A reading position
waits 2 seconds before it reaches storage, so a scroll causes one write and not one
write for each reported position. ReaderScreen writes the position that still waits
before it closes.The reader runs inside a web view, and an EPUB publication is untrusted input. The library therefore applies these rules:
blob:, data: and about: schemes,
because the engine puts each section of the publication into an iframe with a blob:
URL. Remote content inside a publication does not load... is rejected.adb shell setprop log.tag.Foliate DEBUG, or with the FOLIATE_DEBUG environment
variable in the Xcode scheme. A warning or an error always reaches the log.MIT License. See LICENSE.
The artifacts contain a copy of foliate-js, zip.js and fflate. See THIRD-PARTY-NOTICES.md for the full license text of each one.
To report a vulnerability privately, see SECURITY.md.
See CONTRIBUTING.md.
foliate-kmp is a Kotlin Multiplatform (Compose Multiplatform) EPUB reader library. It uses foliate-js as its engine. It gives you standards-compliant EPUB rendering with discrete pagination, CFI navigation, annotations, search and themes on Android and iOS.
Stability: beta. The public API can change in a minor release until version 1.0. Each change appears in CHANGELOG.md.
| Sample Library | Reading Experience | Typography & Themes | Table of Contents |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
| Artifact | Description |
|---|---|
io.github.asadullah012:foliate-kmp-core |
Headless engine, WebView platform bridges (Android WebViewAssetLoader, iOS WKURLSchemeHandler), EpubReaderController, data models and storage interfaces. |
io.github.asadullah012:foliate-kmp-compose |
Complete Material 3 reading screen with a top navigation bar, a bottom progress scrubber, a TOC drawer, an appearance sheet, an annotations dialog, a footnote sheet and in-book search. |
| Target | Supported | Note |
|---|---|---|
| Android | Yes |
minSdk 26, compileSdk 37 |
iOS device (iosArm64) |
Yes | |
iOS simulator, Apple silicon (iosSimulatorArm64) |
Yes | |
iOS simulator, Intel (iosX64) |
No | Compose Multiplatform publishes no iosX64 artifacts. |
| Desktop, JVM, web | No |
Add the library to your build.gradle.kts:
// Option 1: the complete reader screen (this includes the core)
commonMain.dependencies {
implementation("io.github.asadullah012:foliate-kmp-compose:0.1.0-beta01")
}
// Option 2: the headless engine only, for your own UI
commonMain.dependencies {
implementation("io.github.asadullah012:foliate-kmp-core:0.1.0-beta01")
}You need no extra configuration. foliate-kmp-core carries its own consumer rules
inside the AAR.
import androidx.compose.runtime.Composable
import io.github.asadullah012.foliate.compose.ui.ReaderScreen
@Composable
fun MyBookScreen(epubFilePath: String, onBack: () -> Unit) {
ReaderScreen(
filePath = epubFilePath,
bookTitle = "Pride and Prejudice",
onNavigateBack = onBack
)
}Use this to build your own reader UI, toolbars and gesture controls:
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.fillMaxSize
import io.github.asadullah012.foliate.EpubReaderController
import io.github.asadullah012.foliate.model.EpubReaderConfig
import io.github.asadullah012.foliate.model.EpubReaderTheme
import io.github.asadullah012.foliate.ui.FoliateReaderView
@Composable
fun CustomReader(bookPath: String) {
val controller = remember { EpubReaderController() }
FoliateReaderView(
bookPath = bookPath,
controller = controller,
config = EpubReaderConfig(theme = EpubReaderTheme.SEPIA, fontSize = 20),
modifier = Modifier.fillMaxSize()
)
}The repository includes a runnable Compose Multiplatform sample app in the :sample module. It bundles a public-domain copy of Alice's Adventures in Wonderland to demonstrate pagination, themes, CFI restoration, search, and text annotations out of the box.
./gradlew :sample:installDebugOpen the Xcode project in Xcode and click Run:
open sample/iosApp/iosApp.xcodeprojOr build the framework for the Apple Silicon simulator:
./gradlew :sample:compileKotlinIosSimulatorArm64EpubReaderStorage to save progress, bookmarks
and highlights to Room, SQLite, DataStore or a remote database. A reading position
waits 2 seconds before it reaches storage, so a scroll causes one write and not one
write for each reported position. ReaderScreen writes the position that still waits
before it closes.The reader runs inside a web view, and an EPUB publication is untrusted input. The library therefore applies these rules:
blob:, data: and about: schemes,
because the engine puts each section of the publication into an iframe with a blob:
URL. Remote content inside a publication does not load... is rejected.adb shell setprop log.tag.Foliate DEBUG, or with the FOLIATE_DEBUG environment
variable in the Xcode scheme. A warning or an error always reaches the log.MIT License. See LICENSE.
The artifacts contain a copy of foliate-js, zip.js and fflate. See THIRD-PARTY-NOTICES.md for the full license text of each one.
To report a vulnerability privately, see SECURITY.md.
See CONTRIBUTING.md.