Retool has a built-in dark mode you can enable in the Theme Editor (App Settings → Appearance → Theme → select Dark preset). Users can also control their editor display mode in Personal Settings. For a user-togglable in-app dark mode, use a Temporary State variable with a Toggle component and CSS class switching. Custom CSS overrides need !important to work in dark mode.
| Fact | Value |
|---|---|
| Tool | Retool |
| Difficulty | Beginner |
| Time required | 10-15 min |
| Compatibility | Retool Cloud and Self-hosted |
| Last updated | March 2026 |
Three Approaches to Dark Mode in Retool
Retool offers dark mode through its built-in Theme Editor, which provides a Dark preset that switches the entire app's color palette. This is the simplest approach: one click in App Settings changes all components to use dark backgrounds, light text, and appropriately adjusted borders.
Beyond the static theme, you can build a dynamic user-togglable dark mode using a Temporary State variable and CSS class switching. When the user clicks a Toggle, the app body class changes, and CSS rules handle the visual transformation.
For quick prototyping or edge cases, the CSS filter trick (`body { filter: invert(1) hue-rotate(180deg); }`) inverts all colors instantly — useful for testing but not production-ready.
This tutorial covers all three approaches with specific Retool UI paths and CSS syntax.
Prerequisites
- A Retool app with components you want to display in dark mode
- Editor or Admin access to the Retool app
- Basic understanding of Retool's App Settings panel
- Optional: familiarity with CSS variables for advanced customization
Step-by-step guide
Enable the built-in Dark theme via Theme Editor
In the Retool editor, click the gear icon in the top toolbar to open App Settings. Go to Appearance → Theme. Click the Theme Editor button. In the Theme Editor modal, look for the Theme preset selector at the top — it shows Default (light). Click it and select Dark. Click Save to apply. The entire app canvas instantly switches to a dark color scheme including backgrounds, text, borders, and component surfaces.
Expected result: The app canvas, all components, and the background switch to dark colors immediately after saving the theme.
Customize dark mode color tokens
With the Dark preset active in the Theme Editor, you can override individual color tokens. The Theme Editor exposes CSS variable tokens like --retool-color-background-primary, --retool-color-text-primary, and --retool-color-accent. Click any color swatch in the Theme Editor to open a color picker and set a custom value. For more precise token control, add CSS variables in App Settings → Custom CSS:
1/* Override specific dark mode color tokens */2:root {3 /* Custom dark background — slightly lighter than Retool default */4 --retool-color-background-primary: #1a1b2e !important;5 6 /* Custom accent color in dark mode */7 --retool-color-accent: #7C3AED !important;8 9 /* Text color */10 --retool-color-text-primary: #E2E8F0 !important;11}Expected result: The dark theme uses your custom color values for backgrounds and accents throughout the app.
Create a user-togglable dark mode switch
For apps where users can toggle dark/light mode at runtime, you need a Temporary State variable and a Toggle component. First, create a Temporary State: in the editor, open the State panel (or press S key in the editor) and add a new Temporary State named darkMode with a default value of false. Next, add a Toggle component to the app. In the Toggle's Inspector, set the Default value to `{{ darkMode.value }}`. Add an event handler: On Change → Run Script → enter: `await darkMode.setValue(!darkMode.value);`. Note: setValue() is async — do not read darkMode.value immediately after setting it in the same script execution context.
1// Toggle component event handler (On Change → Run Script)2// Note: setValue is async — the new value is not immediately available3await darkMode.setValue(!darkMode.value);45// Do NOT do this — darkMode.value will still be the old value here:6// await darkMode.setValue(!darkMode.value);7// console.log(darkMode.value); // still old value!Expected result: The Toggle component changes the darkMode temporary state value when clicked.
Apply dark mode CSS based on the state variable
With the darkMode temporary state variable controlling a class on the body, add CSS rules to App Settings → Custom CSS that activate dark styles when the class is present. Use the conditional CSS class approach: when darkMode is true, add a class to the app container, then write CSS targeting that class. Alternatively, combine with a Text component that dynamically sets a CSS class: In a Text component's CSS Class Name (Inspector → Advanced), enter: `{{ darkMode.value ? 'dark-mode-active' : '' }}` Then write CSS for .dark-mode-active descendants:
1/* Dark mode styles activated by .dark-mode-active class */23/* When dark mode is on, override app background */4.dark-mode-active ~ ._retool-app-body,5body._dark-mode ._retool-app-body {6 background-color: #1a1b2e !important;7 color: #E2E8F0 !important;8}910/* Table in dark mode */11.dark-mode-active ._retool-table,12.dark-mode-active ._retool-table thead tr {13 background-color: #2d2f3e !important;14 color: #E2E8F0 !important;15 border-color: #4a4d6b !important;16}1718.dark-mode-active ._retool-table tbody tr:hover {19 background-color: #3a3d52 !important;20}Expected result: When darkMode.value is true, the app body and table components display dark colors.
Use the CSS filter quick hack for prototyping
For rapid prototyping or testing what a dark mode would look like, you can use a CSS filter inversion trick. In App Settings → Custom CSS, add: ```css body { filter: invert(1) hue-rotate(180deg) !important; } /* Re-invert images so they look correct */ img, video, ._retool-image { filter: invert(1) hue-rotate(180deg) !important; } ``` This inverts all colors and then rotates hues by 180 degrees to restore natural-looking colors while darkening backgrounds. White becomes black, light gray becomes dark gray. It's not perfect — some colors look off — but it's useful for a 30-second dark mode preview.
Expected result: The entire app inverts its colors instantly. Backgrounds darken and text appears light.
Handle custom CSS rules in dark mode context
If you have existing custom CSS rules (from other styling work), review them for dark mode compatibility. Rules that hardcode light colors (e.g., background-color: #ffffff !important) will break the dark theme. Use CSS custom properties to make your color rules theme-aware: Define them for both light and dark, then reference them in component rules:
1/* Theme-aware CSS custom properties */2:root {3 --app-surface: #ffffff;4 --app-text: #1a1a1a;5 --app-border: #e2e8f0;6}78/* Dark mode overrides — triggered by .dark-mode-active class or dark theme */9@media (prefers-color-scheme: dark) {10 :root {11 --app-surface: #1a1b2e;12 --app-text: #e2e8f0;13 --app-border: #4a4d6b;14 }15}1617/* Using the variables in component styles */18._retool-container {19 background-color: var(--app-surface) !important;20 color: var(--app-text) !important;21 border-color: var(--app-border) !important;22}Expected result: Color values automatically adapt between light and dark modes without hardcoded hex values in component rules.
Complete working example
1/* ========================================2 Retool Dark Mode — Toggle Implementation3 Requires: darkMode temporary state variable4 CSS class: dark-mode-active on wrapper element5 ======================================== */67/* Light mode defaults (CSS custom properties) */8:root {9 --app-bg: #f9fafb;10 --app-surface: #ffffff;11 --app-text: #111827;12 --app-text-muted: #6b7280;13 --app-border: #e5e7eb;14 --app-accent: #7C3AED;15}1617/* Dark mode overrides */18.dark-mode-active {19 --app-bg: #0f172a;20 --app-surface: #1e293b;21 --app-text: #f1f5f9;22 --app-text-muted: #94a3b8;23 --app-border: #334155;24 --app-accent: #a78bfa;25}2627/* Apply to app body */28._retool-app-body {29 background-color: var(--app-bg) !important;30 color: var(--app-text) !important;31}3233/* Tables */34._retool-table {35 background-color: var(--app-surface) !important;36 border-color: var(--app-border) !important;37}3839._retool-table thead tr {40 background-color: var(--app-bg) !important;41}4243._retool-table tbody tr:hover {44 background-color: var(--app-border) !important;45}4647/* Text components */48._retool-text {49 color: var(--app-text) !important;50}5152/* Inputs */53._retool-textInput input {54 background-color: var(--app-surface) !important;55 color: var(--app-text) !important;56 border-color: var(--app-border) !important;57}5859/* Containers */60._retool-container {61 background-color: var(--app-surface) !important;62 border-color: var(--app-border) !important;63}Common mistakes when enabling Dark Mode in Retool
Why it's a problem: Using setValue() to toggle darkMode and immediately reading darkMode.value to update CSS, getting the old value
How to avoid: setValue() is async. Pass the new value directly to subsequent logic rather than re-reading: const newVal = !darkMode.value; await darkMode.setValue(newVal); — then use newVal locally.
Why it's a problem: Applying dark mode via a theme preset but still having hardcoded white backgrounds in custom CSS that override the dark theme
How to avoid: Audit your Custom CSS for any hardcoded light colors (background: #fff, color: #000) and replace them with CSS custom properties that respond to the dark mode class.
Why it's a problem: Forgetting that !important is needed even in dark mode CSS overrides
How to avoid: CSS rules targeting ._retool-* selectors still need !important in dark mode, just as in light mode. Retool's component styles have high specificity regardless of theme.
Best practices
- Use Retool's built-in Dark theme preset for the simplest dark mode — it handles all component colors automatically
- When building a user-togglable dark mode, store the preference in localStorage so it persists across sessions
- Use CSS custom properties (variables) for color values so light/dark switching is a one-line change per token
- Remember that setValue() is async — don't read darkMode.value immediately after calling await darkMode.setValue()
- Test dark mode with images and charts — they often need separate treatment (images: filter: brightness(0.9); charts: may need custom theme colors)
- Add re-inversion rules for any image or video elements if using the CSS filter dark mode hack
- Don't hardcode hex colors in component CSS rules — always use CSS custom properties that you can override for dark mode
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I'm building a Retool app and want to implement dark mode. I want a Toggle button that users can click to switch between light and dark modes, and I want the preference to persist. Explain: (1) how to create a darkMode Temporary State variable in Retool, (2) how to wire the Toggle's On Change event handler using setValue() noting it's async, (3) what CSS I need in the Custom CSS section to style dark mode, and (4) how to save the preference to localStorage.
Add a dark mode toggle to this Retool app: (1) create a Temporary State variable named darkMode defaulting to false, (2) add a Toggle component with On Change handler that calls await darkMode.setValue(!darkMode.value), (3) add CSS in Custom CSS that activates dark backgrounds and light text when the dark-mode-active class is present on the wrapper. Use !important on all color overrides.
Frequently asked questions
Can users set their own dark mode preference in Retool without me building a toggle?
Yes — Retool users can set their personal display preference in their Personal Settings (click profile avatar → Personal Settings → Appearance). This controls the Retool editor and app display for their account only. However, this only works for logged-in Retool users; external users on public apps cannot change this setting.
Does Retool's dark theme work with custom CSS I've already written?
Partially. Retool's built-in dark theme switches its own component colors, but your custom CSS rules with hardcoded hex values will still apply as written. Audit your Custom CSS for light-color hardcodes (e.g., background-color: white !important) and replace them with CSS custom properties that you can override for dark mode.
Is there a way to automatically detect the user's OS dark mode preference in Retool?
Yes, using the CSS @media (prefers-color-scheme: dark) query in your Custom CSS. Add your dark mode overrides inside this media query and they'll activate automatically based on the user's OS setting — no toggle needed. Combine with JavaScript window.matchMedia('(prefers-color-scheme: dark)').matches to initialize the darkMode state variable.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation