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 |
[!WARNING] Correction (Sept 2026). Two things in this post are wrong on Joplin 3.7.x β see the follow-up below before copying the CSS.
icon-shareis not unique to Toggle external editing.- Don't use the
.toolbar-button .icon-share"fallback" rule.
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.
This is noble work, but to me it provides another reason why toolbar customization shouldn't require CSS. This solution includes more than 600 words of tutorial for an operation which could be achieved by the click of a button. A simple add/remove button in the GUI seem far more reasonable a solution to me, and surely for most non-technical users who'd rather not mess with devtools or CSS.
Does anyone else agree this would be a useful feature to be implemented in the desktop GUI, as well as mobile?
Following up on my own guide above β two corrections, plus a warning about a failure mode that looks like the CSS but isn't.
1. icon-share is not unique. I said it identifies Toggle external editing. On Joplin 3.7.18 three commands declare iconName: "icon-share":
toggleExternalEditingβ "Toggle external editing"startExternalEditingβ "Open in external editor"openNoteInNewWindowβ "Open in new window"
So button.toolbar-button:has(.icon-share) hides all three. Target the tooltip instead:
button.toolbar-button[title="Toggle external editing"] {
display: none !important;
}
2. Drop the :has() fallback. Some copies of my snippet include:
.toolbar-button .icon-share { display: none !important; } /* don't */
:has() works in every Electron version Joplin 3.x ships, so this is dead weight β and if it ever did apply it would hide the glyph while leaving an invisible, still-clickable button.
3. If your toolbar icons stop matching their functions, it is almost certainly not this CSS.
I hit exactly that after upgrading to 3.7.18 and wasted time suspecting my own stylesheet. Worth knowing why that's a dead end: :nth-child counts elements hidden with display: none. Hiding a toolbar button cannot shift any index, and an icon and its click handler live on the same <button> β CSS can't separate them.
The real cause in my case was the macOS theme plugin, which hides every native TinyMCE icon (.tox-toolbar__group svg { display: none !important; }) and repaints them by position. Joplin 3.7.x added joplinLinkToNote as a 5th button to one toolbar group; the theme only maps 4, so every icon after it was painted one place early and the last one came out blank. Filed upstream β it's a recurrence of ajilderda/joplin-macos-native-theme#173.
Quickest way to tell them apart: hover the button and read the tooltip. Tooltip matches the action but not the icon β a theme is repainting icons positionally. Tooltip, icon and action all agree but the button is somewhere unexpected β that's just reflow from hiding buttons.
And to Petr0s's point β this thread is now a worked example of the argument. A GUI toggle wouldn't have a stylesheet-versus-plugin failure mode at all.