A powerful Kotlin Multiplatform library and CLI tool to convert Feishu (Lark) documents to beautiful, standalone HTML files.
🌐 Now supports JVM, JS, and Native platforms!
📦 Download Latest Release - Pre-built binaries for macOS, Linux, Windows
📖 View API Documentation - Complete KDoc reference
🚀 Maven Central - Use as library dependency
- 🌐 Kotlin Multiplatform - Runs on JVM, JS (Node.js/Browser), and Native platforms
- 🎯 Comprehensive Block Support - All major Feishu document block types (headings, paragraphs, lists, tables, code blocks, etc.)
- 📦 Resource Management - Automatic download and save of images and attachments
- 🖼️ Base64 Image Embedding - Optional base64 encoding for standalone HTML files
- 🎨 Rich Text Formatting - Full support for text styles (bold, italic, underline, strikethrough, links, etc.)
- 📎 Enhanced File Attachments - Beautiful Feishu-style attachment cards with type icons
- 🧮 Math Rendering - Mathematical formulas powered by MathJax
- 💻 Syntax Highlighting - Code blocks with 70+ language support
- 🎨 Customizable Templates - Default, Fragment, and Full HTML template modes
- 🔧 Flexible Usage - Use as library or CLI tool with rich options
- ⚡ Async Downloads - Asynchronous resource downloading for better performance
- 🛡️ Type Safety - Type-safe HTML generation using kotlinx.html DSL
- 🎭 Clean Architecture - Elegant Renderer delegation pattern
- 🚀 Cross-Platform - >95% code shared across all platforms
- 🧹 Clean Output - Optional hiding of unsupported block warnings
Feishu2HTML faithfully recreates the original styling and layout. Side-by-side comparison:
| Feishu Online | Local HTML Output |
|---|---|
![]() |
![]() |
The generated HTML preserves typography, colors, layout, and all media content with 100% authentic Feishu appearance.
- Feishu2HTML
- JVM: JDK 17+, Gradle 8.0+ (or use included wrapper)
- JS: Node.js 16+ (browser not supported)
- Native: Platform toolchain (XCode/GCC/MinGW)
- Visit Feishu Open Platform
- Create a self-built app
- Get your
App IDandApp Secret - Add the following permissions to your app:
docx:document- View, comment, and export documentsdrive:drive- View and download files in cloud storage
Important: You must grant your app access to the documents you want to export:
- Open the Feishu document you want to export
- Click the "Share" button in the top-right corner
- Add your app/bot to the document collaborators
- Grant at least "View" permission
Download pre-built binaries from GitHub Releases:
- macOS Apple Silicon:
feishu2html-{version}-macosArm64.tar.gz - macOS Intel:
feishu2html-{version}-macosX64.tar.gz - Linux x64:
feishu2html-{version}-linuxX64.tar.gz - Windows x64:
feishu2html-{version}-mingwX64.zip - JVM JAR (cross-platform):
feishu2html-{version}-jvm.jar
Or use Maven Central to add as a library dependency.
Using JAR (Recommended):
Download the JVM JAR from GitHub Releases:
# Download
curl -L -O https://github.com/yidafu/feishu2html/releases/latest/download/feishu2html-1.0.2-jvm.jar
# Basic usage - Export a single document
java -jar feishu2html-1.0.2-jvm.jar <app_id> <app_secret> <document_id>
# Export multiple documents
java -jar feishu2html-1.0.2-jvm.jar <app_id> <app_secret> <doc_id_1> <doc_id_2> <doc_id_3>
# With options for standalone HTML (embedded images and CSS)
java -jar feishu2html-1.0.2-jvm.jar --inline-images --inline-css <app_id> <app_secret> <document_id>
# Clean output (hide unsupported block warnings)
java -jar feishu2html-1.0.2-jvm.jar --hide-unsupported <app_id> <app_secret> <document_id>
# All options combined
java -jar feishu2html-1.0.2-jvm.jar -t fragment --inline-images --inline-css --hide-unsupported <app_id> <app_secret> <document_id>CLI Options:
Options:
-t, --template <mode> HTML template mode: default | fragment | full
--inline-images Embed images as base64 data URLs
--inline-css Embed CSS styles inline in <style> tag
--hide-unsupported Hide unsupported block type warnings
-h, --help Show help message
Using Gradle (from source):
# Clone the repository
git clone https://github.com/yidafu/feishu2html.git
cd feishu2html
# Build and run
./gradlew runJvm --args="<app_id> <app_secret> <document_id>"
./gradlew runJvm --args="--inline-images --inline-css <app_id> <app_secret> <document_id>"Example:
java -jar feishu2html-1.0.2-jvm.jar cli_a1234567890abcde your_app_secret_here doxcnABC123XYZ456Output files will be saved to ./output/ directory by default.
Kotlin Multiplatform Project:
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation("dev.yidafu.feishu2html:feishu2html:1.0.2")
}
}
}
}JVM-only Project:
dependencies {
implementation("dev.yidafu.feishu2html:feishu2html-jvm:1.0.2")
}JS Project:
dependencies {
implementation("dev.yidafu.feishu2html:feishu2html-js:1.0.2")
}import dev.yidafu.feishu2html.Feishu2Html
import dev.yidafu.feishu2html.Feishu2HtmlOptions
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
val options = Feishu2HtmlOptions(
appId = "your_app_id",
appSecret = "your_app_secret"
)
// Use .use {} for automatic resource management
Feishu2Html(options).use { converter ->
converter.export("doxcnABC123XYZ456")
println("Export completed successfully!")
}
}import dev.yidafu.feishu2html.Feishu2Html
import dev.yidafu.feishu2html.Feishu2HtmlOptions
import dev.yidafu.feishu2html.TemplateMode
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
val options = Feishu2HtmlOptions(
appId = "your_app_id",
appSecret = "your_app_secret",
outputDir = "./output", // HTML output directory
imageDir = "./output/images", // Image save directory
fileDir = "./output/files", // Attachment save directory
imagePath = "images", // Relative path for images in HTML
filePath = "files", // Relative path for files in HTML
externalCss = false, // Inline CSS (true = external file)
customCss = null, // Custom CSS (optional)
templateMode = TemplateMode.DEFAULT, // HTML template mode
inlineImages = true, // Embed images as base64
showUnsupportedBlocks = false // Hide unsupported block warnings
)
// Automatic resource cleanup with use {}
Feishu2Html(options).use { converter ->
// Batch export multiple documents
val documentIds = listOf(
"doxcnABC123XYZ456",
"doxcnDEF789GHI012",
"doxcnJKL345MNO678"
)
converter.exportBatch(documentIds)
println("Batch export completed!")
}
}Available Options:
| Option | Type | Default | Description |
|---|---|---|---|
appId |
String | required | Feishu application App ID |
appSecret |
String | required | Feishu application App Secret |
outputDir |
String | "./output" |
HTML output directory |
imageDir |
String | "./output/images" |
Image save directory |
fileDir |
String | "./output/files" |
Attachment save directory |
imagePath |
String | "images" |
Relative path for images in HTML |
filePath |
String | "files" |
Relative path for files in HTML |
customCss |
String? | null |
Custom CSS styles |
externalCss |
Boolean | true |
Use external CSS file (false = inline) |
cssFileName |
String | "feishu-style-optimized.css" |
CSS filename |
templateMode |
TemplateMode | DEFAULT |
HTML template mode |
inlineImages |
Boolean | false |
Embed images as base64 data URLs |
showUnsupportedBlocks |
Boolean | true |
Show unsupported block warnings |
By default, exported HTML uses an optimized version extracted from official Feishu CSS:
val options = Feishu2HtmlOptions(
appId = "your_app_id",
appSecret = "your_app_secret",
externalCss = true, // Use external CSS file (default)
cssFileName = "feishu-style-optimized.css" // Optimized CSS (default, 16KB)
)
Feishu2Html(options).use { converter ->
converter.export("document_id")
}
// Output:
// - document.html (with <link> to CSS)
// - feishu-style-optimized.css (optimized styles, only 16KB!)Why Optimized CSS? 98.4% smaller (16KB vs 1MB), faster load times, only needed selectors, 100% authentic appearance.
Use inline CSS for single-file portability (no separate CSS file):
val options = Feishu2HtmlOptions(
appId = "your_app_id",
appSecret = "your_app_secret",
externalCss = false // Embed CSS in <style> tag
)Override with your own CSS (requires inline mode):
val customCss = """
.protyle-wysiwyg { font-family: "Inter", sans-serif; }
.heading-h1 { color: #2c3e50; border-bottom: 3px solid #3498db; }
/* Add more custom styles... */
""".trimIndent()
val options = Feishu2HtmlOptions(
appId = "your_app_id",
appSecret = "your_app_secret",
externalCss = false, // Must use inline mode
customCss = customCss
)Detailed platform-specific usage guides:
- JVM Usage Guide - Complete guide for JVM platform (CLI & Library)
- Node.js Usage Guide - CommonJS setup and usage
- macOS Usage Guide - Native macOS executable (ARM64 & Intel)
- Linux Usage Guide - Native Linux executable (x64)
- Windows Usage Guide - Native Windows executable (x64)
Feishu2HTML is built with Kotlin Multiplatform, enabling it to run on multiple platforms from a single codebase.
| Platform | Status | Notes |
|---|---|---|
| JVM | ✅ Production Ready | Full features (Library + CLI) |
| JS (Node.js) | ✅ Fully Supported | Core library features |
| Native (macOS) | ✅ Verified | Core library features, tested on Intel & Apple Silicon |
| Native (Linux x64) | 🔄 Experimental | Core library features |
| Native (Windows x64) | 🔄 Experimental | Core library features |
| iOS | 🔄 Experimental | Core library features |
- JVM: Full support including CLI tool, Logback logging, and complete file I/O
- JS (Node.js): File system via Node.js fs module, Ktor client, browser not supported
- Native: POSIX file I/O, platform-specific HTTP engines (Darwin/Curl)
Build for specific platforms:
# JVM
./gradlew compileKotlinJvm
./gradlew jvmJar
# JS
./gradlew compileKotlinJs
./gradlew jsJar
# Native (macOS ARM64)
./gradlew compileKotlinMacosArm64
# All platforms
./gradlew buildAll platforms use the same API (see Library Usage for examples):
- JVM: Use
runBlockingcoroutine scope - JS (Node.js): Use
GlobalScope.promisefor async execution - Native: Use
runBlockingcoroutine scope
The document ID can be extracted from the Feishu document URL:
https://example.feishu.cn/docx/doxcnABC123XYZ456
└─────────────────┘
Document ID
For example:
- URL:
https://company.feishu.cn/docx/TPDddjY5foJZ8axlf9fctf2Wnse - Document ID:
TPDddjY5foJZ8axlf9fctf2Wnse
Error: Failed to get token: app access token invalid
Solutions:
- Verify
app_idandapp_secretare correct - Ensure the app is enabled and published
- Check app permissions are configured
Error: Failed to get document content: no permission
Solutions:
- Confirm app has required permissions
- Ensure document is accessible to the app
- Try adding the app to document collaborators
Solutions:
- Check network connection
- Confirm app has
drive:drivepermission - Some legacy documents may have API limitations
When a Feishu document contains references to other documents (e.g., embedded documents, links to other docs), you must grant your app access to all referenced documents as well. The tool cannot automatically propagate permissions.
Workaround: Manually share each referenced document with your app before exporting.
Content from real-time collaboration features (e.g., comments, suggestions) is not included in the export.
Some advanced block types are not yet supported (ISV, Mindnote, Sheet, Task, OKR, Wiki Catalog, Agenda, Link Preview, etc.). By default, these will display a placeholder message for debugging purposes.
Solution: Use the --hide-unsupported CLI option or set showUnsupportedBlocks = false in library usage to hide these warnings for cleaner output.
# CLI: Hide unsupported block warnings
java -jar feishu2html.jar --hide-unsupported <app_id> <app_secret> <document_id>// Library: Hide unsupported block warnings
val options = Feishu2HtmlOptions(
appId = "your_app_id",
appSecret = "your_app_secret",
showUnsupportedBlocks = false // Hide warnings
)See the Supported Block Types table for a complete list.
The Feishu API has rate limits. The tool includes built-in rate limiting (QPS=2) to avoid exceeding limits, but very large documents may take time to process.
Complete API documentation is automatically generated and published to GitHub Pages:
🔗 View Online API Documentation
The documentation is automatically updated on every push to the main branch.
- JVM Usage Guide - Detailed JVM platform guide
- Node.js Usage Guide - Node.js / JavaScript guide
- macOS Usage Guide - Native macOS executable guide
- Linux Usage Guide - Native Linux executable guide
- Windows Usage Guide - Native Windows executable guide
| Block Type | Type Code | Support Status | Notes |
|---|---|---|---|
| Page | 1 | ✅ Full | - |
| Text | 2 | ✅ Full | - |
| Heading 1-9 | 3-11 | ✅ Full | All 9 levels supported |
| Bullet List | 12 | ✅ Full | - |
| Ordered List | 13 | ✅ Full | - |
| Code Block | 14 | ✅ Full | 70+ languages |
| Quote | 15 | ✅ Full | - |
| Equation | 16 | ✅ Full | MathJax rendering |
| Todo | 17 | ✅ Full | - |
| Bitable | 18 | Placeholder only | |
| Callout | 19 | ✅ Full | - |
| Chat Card | 20 | Placeholder only | |
| Diagram | 21 | ✅ Full | - |
| Divider | 22 | ✅ Full | - |
| File | 23 | ✅ Full | - |
| Grid | 24 | ✅ Full | Column layout |
| Grid Column | 25 | ✅ Full | - |
| Iframe | 26 | ✅ Full | Multiple embed types |
| Image | 27 | ✅ Full | - |
| ISV | 28 | ❌ Unsupported | - |
| Mindnote | 29 | ❌ Unsupported | - |
| Sheet | 30 | ❌ Unsupported | - |
| Table | 31 | ✅ Full | - |
| Table Cell | 32 | ✅ Full | - |
| View | 33 | ❌ Unsupported | - |
| Quote Container | 34 | ✅ Full | - |
| Task | 35 | ❌ Unsupported | - |
| OKR | 36 | ❌ Unsupported | - |
| OKR Objective | 37 | ❌ Unsupported | - |
| OKR Key Result | 38 | ❌ Unsupported | - |
| OKR Progress | 39 | ❌ Unsupported | - |
| Add-ons | 40 | ❌ Unsupported | Plugin components |
| Jira Issue | 41 | ❌ Unsupported | - |
| Wiki Catalog | 42 | ❌ Unsupported | Legacy wiki subpage list |
| Board | 43 | ✅ Full | Electronic whiteboard |
| Agenda | 44 | ❌ Unsupported | - |
| Agenda Item | 45 | ❌ Unsupported | - |
| Agenda Item Title | 46 | ❌ Unsupported | - |
| Agenda Item Content | 47 | ❌ Unsupported | - |
| Link Preview | 48 | ❌ Unsupported | - |
| Source Synced | 49 | ❌ Unsupported | - |
| Reference Synced | 50 | ❌ Unsupported | - |
| Sub Page List | 51 | ❌ Unsupported | Wiki subpage list (new) |
| AI Template | 52 | ❌ Unsupported | - |
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! See CONTRIBUTING.md for architecture, coding standards, and PR guidelines.
This project was inspired by feishu2md.
20:53:59.100 [main] INFO dev.yidafu.feishu2html.api.FeishuAuthService -- Requesting new tenant_access_token 20:53:59.100 [main] DEBUG dev.yidafu.feishu2html.api.FeishuAuthService -- App ID: cli_a8790687b4bdd01c 20:53:59.101 [main] DEBUG dev.yidafu.feishu2html.api.FeishuAuthService -- App Secret: 3sXNpzmX4ErVg07gNMOgdMkQn2usPg
20:54:47.596 [main] INFO dev.yidafu.feishu2html.api.FeishuAuthService -- Requesting new tenant_access_token 20:54:47.596 [main] DEBUG dev.yidafu.feishu2html.api.FeishuAuthService -- App ID: cli_a8790687b4bdd01c 20:54:47.596 [main] DEBUG dev.yidafu.feishu2html.api.FeishuAuthService -- App Secret: 3sxXNpzmX4ErVg07gNMOgdMkQn2usPgq

