Are there any plans to add the ability to customize the toolbar in the desktop app, now that this feature is available on mobile?
I'd really like this feature too! I've scoured the forums & it appears CSS editing is currently required to adjust/remove the buttons in the toolbar on desktop. Someone else requested this feature ages back, but it didn't get much love from the community: [Feature Request] Toolbar move button locations? & Viewer mode - plugin button shows as gear icon - #6 by laurent
Here's hoping we can have settings in the app (rather than editing CSS) to customise the toolbar. Making customisation more user-friendly would surely be a big win for new users to Joplin & the non-technical among us.
I put Claude on the task for hiding toggle external editing, superscript, subscript, and insert time. Now, the 17.6M cache-read number includes the consumption of my existing environment, which is maybe 10% of that number.
Time
- Wall-clock span: ~55 minutes (08:15 β 09:11 PDT)
- Note: this is elapsed time from first to last message, not continuous active work β it includes the gaps while you tested in Joplin and switched the model.
Tokens
| Category | Tokens |
|---|---|
| Input (non-cached) | 619 |
| Output (generated) | 134,659 |
| Cache creation (write) | 528,754 |
| Cache read | 17,612,641 |
| Total | 18,276,673 |
Hiding Toolbar Buttons in Joplin via CSS
How to hide buttons in Joplin's editor toolbar (and elsewhere in the app UI) using
userchrome.css. Verified on Joplin 3.6.14 (TinyMCE 6.8.5).
TL;DR
Edit userchrome.css in your Joplin profile directory and add display: none !important; rules. The catch is that Joplin's WYSIWYG toolbar contains two different kinds of buttons, and each needs a different selector:
| Button kind | Examples | How to target |
|---|---|---|
| TinyMCE buttons (the bulk of the Rich Text toolbar) | Bold, Superscript, Subscript, Insert time, headings, lists⦠| .tox .tox-tbtn[title="..."] |
| Joplin React buttons (a few special ones) | Toggle external editing, history back/forward, the Rich Text/Markdown switcher | button.toolbar-button:has(.icon-<name>) |
If a .tox-tbtn[...] selector does nothing, the button is almost certainly a Joplin React button, not a TinyMCE one.
Where the file lives
userchrome.css is in your Joplin profile directory:
| Platform | Path |
|---|---|
| Linux / macOS | ~/.config/joplin-desktop/userchrome.css |
| Windows | %USERPROFILE%\.config\joplin-desktop\userchrome.css |
rootProfileDiris~/.config/joplin-desktopon desktop. Two style files live here:
userchrome.cssβ the application UI (toolbars, panels, sidebar). This is the one for toolbar buttons.userstyle.cssβ only the rendered Markdown/note preview content.
Joplin loads userchrome.css by injecting it as a <link rel="stylesheet" class="dynamic-linked-stylesheet"> into the app's <head>. Changes require a full restart of Joplin (not just switching notes).
The two button types, in detail
1. TinyMCE buttons
Most of the Rich Text toolbar is rendered by TinyMCE (the embedded WYSIWYG engine). TinyMCE renders each button as:
<button class="tox-tbtn" aria-label="Superscript" title="Superscript"> β¦ </button>
Both aria-label and title are set to the button's translated tooltip text. So you can match on either. Always scope with the .tox parent β that's the TinyMCE root container, and scoping avoids accidentally matching anything else:
.tox .tox-tbtn[title="Superscript"],
.tox .tox-tbtn[title="Subscript"],
.tox .tox-tbtn[title="Insert time"] {
display: none !important;
}
Tip β localization:
aria-label/titlecarry the translated tooltip. If your Joplin UI is not in English, use the translated label, or match on a stable structural hook instead (see "Finding the right selector" below).
2. Joplin React buttons
A handful of toolbar buttons are not TinyMCE β Joplin renders them itself as React components. "Toggle external editing" is one of these, which is why .tox-tbtn[...] selectors never touch it. These render as:
<button class="button toolbar-button" title="Toggle external editing">
<span class="toolbar-icon icon-share" role="img" aria-hidden="true"></span>
</button>
The reliable hook is the icon class on the child <span>. Use :has() to select the button that contains that icon:
/* "Toggle external editing" uses the icon-share icon */
button.toolbar-button:has(.icon-share) {
display: none !important;
}
:has() is supported in the Chromium/Electron version Joplin 3.6 ships with, so this works. (If you're on a much older Joplin where :has() doesn't apply, you can hide the icon with .toolbar-button .icon-share { display:none } as a fallback, though that leaves an empty button frame.)
Finding the right selector for any button
Joplin ships Chromium DevTools β use them to read the real markup:
- Help β Toggle Development Tools (or the equivalent in the menu).
- Click the inspector (top-left arrow icon) and click the toolbar button you want to hide.
- Look at the highlighted element:
- If it's
<button class="tox-tbtn" β¦>β it's a TinyMCE button. Note itstitle/aria-labeland use selector type #1. - If it's
<button class="button toolbar-button">with a child<span class="toolbar-icon icon-XXXX">β it's a Joplin React button. Note theicon-XXXXclass and use selector type #2 with:has(.icon-XXXX).
- If it's
- Add the rule to
userchrome.css, save, and restart Joplin.
Verifying the file is even loading
If nothing happens at all, confirm the stylesheet is being picked up before you blame the selector. Drop this obvious rule in temporarily:
/* If the toolbar background turns red after restart, userchrome.css IS loading. */
.tox .tox-toolbar__primary { background: red !important; }
Restart. Red toolbar = file loads, so it's a selector problem. No red = wrong file/path, and you should re-check the profile directory.
Complete working example
This is the exact block that hides Toggle external editing, Superscript, Subscript, and Insert time:
/* Hide WYSIWYG toolbar buttons */
/* toggleExternalEditing β Joplin React button, identified by icon-share child span */
button.toolbar-button:has(.icon-share) {
display: none !important;
}
/* TinyMCE buttons β require .tox parent context; matching on title */
.tox .tox-tbtn[title="Superscript"],
.tox .tox-tbtn[title="Subscript"],
.tox .tox-tbtn[title="Insert time"] {
display: none !important;
}
Gotchas / lessons learned
.tox-tbtnβ every toolbar button. The most common mistake is assuming the whole toolbar is TinyMCE. Toggle external editing, the history arrows, and the editor-type switcher are Joplin React buttons and ignore.tox-tbtnselectors entirely.- Use
!important. Joplin injects its own theme CSS into the same<head>; without!importantyour rule can lose the specificity battle. - Restart fully. The stylesheet is injected at startup; live-editing the file does nothing until you relaunch.
- Tooltip text is translated.
title/aria-labelvalues follow your UI language. For non-English UIs, match the translated string or fall back to a structural selector. - CSS can only show/hide and restyle existing buttons. It cannot add new buttons or reorder them β that needs a plugin or source changes.
- This is UI-version-dependent. Class names (
tox-tbtn,toolbar-button,icon-share) are internal to Joplin/TinyMCE and could change in a future release. If a rule stops working after an upgrade, re-inspect with DevTools.
Verified on Joplin 3.6.14 / TinyMCE 6.8.5. Selector names are internal and may shift between Joplin versions β re-inspect with DevTools if an upgrade breaks a rule.