Import your Kindle highlights and notes from My Clippings.txt directly into Joplin with full multi-language support, preview dialog, and tag extraction.
- Multi-language support: Spanish, English, Italian, French, German, and Portuguese
- Preview dialog: Review and edit highlights before importing
- Editable table: Modify author, book title, and tags before import
- Tag extraction: Convert Kindle notes into Joplin tags (UPPERCASE)
- Bookmark import: Kindle bookmarks are imported as highlights
- Configurable hierarchy: Organize by Author → Book, Book only, or Author only
- Duplicate detection: Avoids creating duplicate notebooks and tags
- Author normalization: "García Márquez, Gabriel" → "Gabriel García Márquez"
- Clipping limit detection: Identifies highlights truncated by Amazon's 10% limit
- Metadata block: Optional metadata at the end of each note
- Open Joplin
- Go to Tools → Options → Plugins
- Search for "KindleToJoplin"
- Click Install
- Download the latest
.jplfile from Releases - Open Joplin
- Go to Tools → Options → Plugins
- Click Install from file
- Select the downloaded
.jplfile
- Connect your Kindle to your computer (or use Kindle for PC/Mac)
- Locate your
My Clippings.txtfile:- Kindle device:
Kindle/documents/My Clippings.txt - Kindle for PC:
Documents/My Kindle Content/My Clippings.txt - Kindle for Mac:
~/Library/Application Support/Kindle/My Kindle Content/My Clippings.txt
- Kindle device:
- In Joplin, go to Tools → Import Kindle Highlights
- Select your
My Clippings.txtfile - Review the highlights in the preview dialog
- Edit any author names, book titles, or tags as needed
- Select which highlights to import
- Click Import Selected
Access settings via Tools → Options → Kindle Import:
| Setting | Description |
|---|---|
| Default clippings file path | Path to your My Clippings.txt file |
| Kindle language | Language of your Kindle device |
| Note hierarchy | How to organize imported highlights |
| Default destination notebook | Root notebook for imports (default: "Kindle Highlights") |
| Include metadata block | Add metadata to each note |
| Setting | Description |
|---|---|
| Note latitude/longitude/altitude | GPS coordinates for imported notes |
| Note author | Author field for imported notes |
| Note source | Source field (default: "kindle-to-joplin") |
| Note source application | Source app field (default: "kindle-to-joplin") |
Kindle Highlights/
└── GARCÍA MÁRQUEZ, GABRIEL/
└── Cien años de soledad/
├── [p.42] Muchos años después...
└── [p.103] El mundo era tan reciente...
Kindle Highlights/
└── Cien años de soledad/
├── [p.42] Muchos años después...
└── [p.103] El mundo era tan reciente...
Kindle Highlights/
└── GARCÍA MÁRQUEZ, GABRIEL/
├── [p.42] Muchos años después...
└── [p.103] El mundo era tan reciente...
Each imported highlight becomes a Joplin note with:
[0042] Muchos años después, frente al pelotón de fusil...
Muchos años después, frente al pelotón de fusilamiento, el coronel
Aureliano Buendía había de recordar aquella tarde remota en que su
padre lo llevó a conocer el hielo.
-----
- date: 15/01/2024 10:30
- author: Gabriel García Márquez
- book: Cien años de soledad
- page: 42
- tags: MEMORIA, TIEMPO
-----Note format: [NNNN] where NNNN is the zero-padded page number (4 digits).
When you add a note to a highlight in Kindle, that note becomes a tag in Joplin. Multiple tags can be separated by commas, semicolons, or periods. Tags are automatically converted to UPPERCASE.
This section documents the technical decisions and patterns used in the project.
src/
├── index.ts # Plugin entry point, registers commands and menu
├── settings.ts # Settings registration with Joplin API
├── parser/ # Domain: Kindle clippings parsing
│ ├── index.ts # Main parser orchestration
│ ├── types.ts # TypeScript interfaces
│ ├── languages.ts # Multi-language regex patterns (Strategy pattern)
│ └── dateParser.ts # Locale-aware date parsing with date-fns
├── importer/ # Domain: Joplin API integration
│ ├── index.ts # Import orchestration
│ └── duplicates.ts # Notebook/tag cache and deduplication
└── ui/ # Presentation layer
├── dialog.ts # Dialog controller (plugin side)
├── dialog.html # Dialog template
├── dialog.css # Styles (light/dark theme support)
└── dialogScript.js # Webview script (sandboxed)
| Pattern | Location | Purpose |
|---|---|---|
| Strategy | languages.ts |
Each language is a configuration object with the same interface, allowing runtime selection |
| Factory | parseEntry() |
Creates KindleClipping objects from raw text |
| Facade | parseClippings() |
Simple API that hides parsing complexity |
| Repository | duplicates.ts |
Caches notebooks/tags to avoid duplicate API calls |
| Builder | generateNoteBody() |
Constructs note content step by step |
1. Separation of Concerns
- Parser knows nothing about Joplin - it only transforms text to structured data
- Importer receives clean data and handles Joplin API
- UI is sandboxed in a webview, communicates via JSON messages
2. Multi-language Support via Strategy Pattern
// Each language defines patterns with the same interface
const spanish: LanguageConfig = {
code: 'es',
patterns: { highlight: /.../, note: /.../, page: /.../ },
dateFormat: "EEEE, d 'de' MMMM 'de' yyyy H:mm:ss"
};Adding a new language only requires adding a new configuration object.
3. Webview Communication
Joplin sanitizes inline scripts, so we use a hidden <div> with JSON data:
<div id="initial-data" style="display:none">{"clippings":[...]}</div>The webview script reads this on load, avoiding bidirectional messaging complexity.
4. Duplicate Detection via Caching Instead of querying the API for each notebook/tag, we fetch all once and cache in memory:
const notebookCache = new Map<string, Folder>();
const tagCache = new Map<string, string>();| Technology | Version | Purpose |
|---|---|---|
| TypeScript | 5.x | Type-safe development |
| Webpack | 5.x | Bundle for Joplin plugin format |
| date-fns | 4.x | Locale-aware date parsing (tree-shakeable) |
| uuid | 10.x | Unique IDs for clippings |
| Jest | 29.x | Unit and integration testing |
| ESLint | 9.x | Code quality (flat config) |
| Prettier | 3.x | Code formatting |
- date-fns: We import only required functions and locales to minimize bundle size
- Locales: Only 6 locales imported (es, en, it, fr, de, pt), not the full locale package
- Tree-shaking: Webpack removes unused code in production builds
| Test Type | Location | Coverage |
|---|---|---|
| Unit tests | test/*.test.ts |
Parser, importer, date parsing, duplicates |
| Integration tests | test/integration.test.ts |
Full parsing workflow with fixture files |
| UI tests | test/standalone.html |
Manual testing with Chrome DevTools |
npm test # Run all tests
npm test -- --watch # Watch mode
npm test -- --coverage # Coverage report (97%+ coverage)- Node.js 18+
- npm
# Clone the repository
git clone https://github.com/KindleTools/KindleToJoplin.git
cd KindleToJoplin
# Install dependencies
npm install
# Build in watch mode
npm run dev
# Build for production
npm run dist
# Run tests
npm test- Build the plugin:
npm run dist - In Joplin, go to Tools → Options → Plugins
- Under Advanced Settings, add the path to the
distfolder - Restart Joplin
| Language | Highlights | Bookmarks |
|---|---|---|
| Spanish | subrayado | marcador |
| English | Highlight | Bookmark |
| Italian | evidenziazione | segnalibro |
| French | surlignement | signet |
| German | Markierung | Lesezeichen |
| Portuguese | destaque | marcador |
If highlights aren't being parsed correctly:
- Check that you've selected the correct language in settings
- Ensure the
My Clippings.txtfile is valid UTF-8 - Check the developer console for error messages (Help → Toggle Development Tools)
The plugin handles UTF-8 with BOM automatically. If you still have encoding issues, try opening the file in a text editor and saving it as UTF-8 without BOM.
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Commit your changes:
git commit -am 'Add new feature' - Push to the branch:
git push origin feature/my-feature - Submit a pull request
Potential enhancements for future versions:
| Feature | Description | Priority |
|---|---|---|
| i18n (Internationalization) | Translate plugin UI to multiple languages (Spanish, German, French, etc.) using i18n library | Medium |
| File → Import menu | Register as import module via joplin.interop.registerImportModule() for native Joplin integration |
Medium |
| GitHub Actions CI | Automated testing and building on pull requests | Low |
| Clipboard import | Paste highlights directly from Kindle app without file | Low |
| Export to Markdown | Export selected highlights to a single Markdown file | Low |
MIT License - see LICENSE for details.