Smarter Tools: Auto-Convert, Keyboard Shortcuts, and Persistent Drafts
This guide has a free tool → Open Base64
# Smarter Tools: Auto-Convert, Keyboard Shortcuts, and Persistent Drafts
We upgraded 20 tools with four quality-of-life improvements that make the entire ToolBox experience faster and smoother. If you have ever clicked a Format button 30 times in one session, or lost your work by accidentally closing a tab, these changes are for you.
This post covers every detail of how the new behavior works, why we made these choices, and how each improvement fits into a real developer workflow.
---
The Problem With "Click to Convert" Tools
Most online developer tools follow a simple pattern: paste your input, click a button, get output. It works. But it creates friction.
Consider a typical workflow with a JSON formatter:
- Open the tool
- Paste your JSON
- Click Format
- See an error because the paste was malformed
- Fix the JSON in another editor
- Paste again
- Click Format again
- Finally get the result
Every click-to-convert tool forces you to context-switch between your hands and your eyes. You look at the input area, look at the button, move to click it, then look at the output area. For a tool you use dozens of times per day, that friction adds up.
The four improvements we shipped address different parts of this friction:
- Auto-convert on input eliminates the button click entirely for most workflows
- Ctrl+Enter shortcut keeps your hands on the keyboard when you do need to trigger manually
- Persistent drafts eliminate the anxiety of accidentally closing a tab
- Consistent copy feedback closes the loop on whether your copy action actually worked
Let us look at each one in depth.
---
Base64 Encoder/Decoder
Base64 encode and decode online - convert text to Base64 or decode Base64 strings instantly, free
JSON Formatter
JSON formatter and validator online - format, beautify, and validate JSON data instantly in your browser
Cron Expression Parser
Free online cron expression parser - parse cron expressions and see the next scheduled run times
Auto-Convert on Input
The biggest change: tools now convert automatically as you type. No more clicking buttons for most use cases.
The 300ms Debounce Strategy
For simple, fast transforms, we use a 300ms debounce. This means the conversion fires 300 milliseconds after you stop typing. If you keep typing, the timer resets.
Tools using 300ms debounce:
- Base64 - encode or decode as you type
- URL Encoder - percent-encode special characters instantly
- Hash Generator - MD5, SHA-1, SHA-256 update live
- Case Converter - camelCase, snake_case, UPPER_CASE without clicking
- HTML Entity Encoder - escape HTML characters in real time
At 300ms, the tool responds fast enough to feel instant, but slow enough that it does not run on every individual keystroke for long documents.
Here is the implementation pattern we used across all 20 tools:
import { useState, useEffect, useRef } from 'react';
function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// Usage in a tool component
function Base64Tool() {
const [input, setInput] = useState('');
const debouncedInput = useDebounce(input, 300);
useEffect(() => {
if (debouncedInput) {
const encoded = btoa(debouncedInput);
setOutput(encoded);
}
}, [debouncedInput]);
return (
<textarea
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Type or paste text to encode..."
/>
);
}The key insight here is that useDebounce returns a stable value that only updates after the delay expires. The actual input state (input) updates on every keystroke for a responsive typing experience. The conversion logic only fires on debouncedInput, which is the quieter, debounced stream.
The Paste-Detection Strategy for Structured Formats
For structured formats like JSON, XML, and YAML, a simple debounce creates a bad experience. If you are typing a JSON object by hand, the tool would show parse errors constantly as you build up the structure:
{ <-- valid start, but shows "Unexpected end of JSON"
{"na <-- garbage state mid-typing
{"name <-- still garbage
{"name": <-- still garbage
{"name": "Jo <-- still garbage
{"name": "John"} <-- finally validShowing five error states before the user finishes typing is noise. It damages trust in the tool and obscures actual errors.
Our solution: detect whether the input came from a paste event. If it was a paste, convert immediately. If it was typed character by character, wait for an explicit trigger (the button click or Ctrl+Enter).
function JsonFormatterTool() {
const [input, setInput] = useState('');
const [output, setOutput] = useState('');
const [error, setError] = useState('');
const isPaste = useRef(false);
function handlePaste() {
isPaste.current = true;
}
function handleChange(e: React.ChangeEvent<HTMLTextAreaElement>) {
const value = e.target.value;
setInput(value);
if (isPaste.current) {
isPaste.current = false;
formatJson(value);
}
}
function formatJson(raw: string) {
try {
const parsed = JSON.parse(raw);
setOutput(JSON.stringify(parsed, null, 2));
setError('');
} catch (err) {
setError(err instanceof Error ? err.message : 'Invalid JSON');
setOutput('');
}
}
return (
<textarea
value={input}
onPaste={handlePaste}
onChange={handleChange}
/>
);
}The isPaste ref acts as a one-shot flag. When the textarea receives a paste event, we set the ref to true. The next onChange event checks the flag, resets it, and fires the conversion. All subsequent onChange events (from typing) are ignored for auto-conversion purposes.
Tools Using Paste Detection
- JSON Formatter - formats on paste, not on every keystroke
- XML Formatter - parses XML trees on paste
- YAML/JSON Converter - detects structured YAML or JSON on paste
- CSV to JSON - parses tabular data on paste
- SQL Formatter - pretty-prints SQL on paste
This split strategy gives you instant results for pasted data while keeping the experience clean when you are typing manually.
Why Not Just Auto-Convert Everything With a Longer Debounce?
We considered using a 1000ms or 2000ms debounce for structured formats. The problem is that users who paste large JSON blobs (20,000+ characters) would experience a 1-2 second delay before seeing any output. With paste detection, the conversion fires immediately after the paste event resolves, regardless of content size.
A 1000ms debounce also means the tool is unresponsive for a full second after every keystroke. If you backspace through a word and then retype it, each backspace resets the timer. The paste-detection approach never interferes with your typing at all.
---
Ctrl+Enter Keyboard Shortcut
Every tool with a primary action button now supports Ctrl+Enter (or Cmd+Enter on Mac). This is the universal shortcut to trigger the main conversion or formatting action.
Why Ctrl+Enter Specifically?
This shortcut has a long history in developer tools:
- Most terminal emulators use Ctrl+Enter to execute commands
- IDEs like JetBrains products use it for "run" actions
- Code playgrounds like CodePen and JSFiddle use Ctrl+Enter to refresh the preview
- Slack and Discord use Shift+Enter for newlines, reserving Enter for "send" - Ctrl+Enter follows similar modal logic
By matching this established pattern, the shortcut requires no learning. Developers who reach for Ctrl+Enter out of muscle memory will find it works exactly as expected.
Implementation
The shortcut is handled at the textarea level so you do not need to move your cursor anywhere:
function useCtrlEnter(callback: () => void) {
function handleKeyDown(e: React.KeyboardEvent) {
const isMac = navigator.platform.toUpperCase().includes('MAC');
const modifier = isMac ? e.metaKey : e.ctrlKey;
if (modifier && e.key === 'Enter') {
e.preventDefault();
callback();
}
}
return { onKeyDown: handleKeyDown };
}
// Usage
function Tool() {
const { onKeyDown } = useCtrlEnter(handleFormat);
return (
<textarea
onKeyDown={onKeyDown}
placeholder="Paste your input here..."
/>
);
}The navigator.platform check ensures Mac users get Cmd+Enter behavior while Windows and Linux users get Ctrl+Enter. We use e.preventDefault() to stop the browser from inserting a newline in the textarea.
Which Tools Support Ctrl+Enter
Every tool with a primary action button supports it. This includes all 20 upgraded tools:
- JSON Formatter - triggers Format
- Diff Checker - triggers Compare
- Regex Tester - triggers Test
- JWT Decoder - triggers Decode
- Code Formatter - triggers Format
- All converter tools - triggers the primary conversion
Keyboard-First Workflows
With Ctrl+Enter in place, a complete keyboard-only workflow for the JSON Formatter looks like this:
- Open the tool (browser keyboard shortcut or bookmarklet)
- Ctrl+A to select all in the textarea (or Tab to focus it)
- Ctrl+V to paste your JSON (triggers auto-format immediately)
- Tab to reach the output
- Ctrl+A, Ctrl+C to copy the formatted output
- Alt+F4 / Cmd+W to close the tab
No mouse required. For developers who live on the keyboard, this is a significant improvement.
---
Persistent Drafts via localStorage
Close a tab by accident? No problem. All 20 upgraded tools now save your input to localStorage with debounced writes. When you return to the tool, your previous input is automatically restored.
The Problem This Solves
Consider this scenario: you are working with a complex Cron expression in the Cron Parser. You have spent five minutes crafting the right schedule, and you understand what each field means. Your coworker sends you a link. You middle-click to open it in a new tab, but accidentally Ctrl+click the Cron Parser tab and close it.
Previously, your cron expression is gone. You have to start over.
With persistent drafts, when you reopen the Cron Parser, your expression is exactly where you left it.
Namespace Convention
Keys are namespaced under toolbox: to avoid conflicts with other apps storing data in the same origin's localStorage:
toolbox:json-formatter:input
toolbox:base64:input
toolbox:base64:mode (encode vs decode)
toolbox:diff-checker:left
toolbox:diff-checker:right
toolbox:hash-generator:algorithmThis convention means you can inspect your browser's localStorage and immediately identify which entries belong to ToolBox. It also prevents accidental overwrites if another tool on the same domain uses a key like input.
The 500ms Write Debounce
Storage writes are debounced at 500ms to avoid performance issues. localStorage writes are synchronous operations that block the main thread. If we wrote on every keystroke in a large textarea, we could cause noticeable jank for users pasting large documents.
function useLocalStorageDraft(key: string, initialValue: string = '') {
const storageKey = `toolbox:${key}`;
const writeTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
// Load initial value from localStorage
const [value, setValue] = useState<string>(() => {
try {
const stored = localStorage.getItem(storageKey);
return stored !== null ? stored : initialValue;
} catch {
return initialValue;
}
});
// Debounced write to localStorage
function updateValue(newValue: string) {
setValue(newValue);
if (writeTimer.current) {
clearTimeout(writeTimer.current);
}
writeTimer.current = setTimeout(() => {
try {
localStorage.setItem(storageKey, newValue);
} catch {
// localStorage may be full or blocked - fail silently
}
}, 500);
}
// Clean up timer on unmount
useEffect(() => {
return () => {
if (writeTimer.current) {
clearTimeout(writeTimer.current);
}
};
}, []);
return [value, updateValue] as const;
}The lazy initializer in useState reads from localStorage only once, when the component mounts. This avoids repeated storage reads on every render.
Handling localStorage Failures Gracefully
localStorage can fail in several scenarios:
- Storage quota exceeded - browsers typically allow 5-10MB per origin
- Private/incognito mode - some browsers block writes in private mode
- Safari ITP - Intelligent Tracking Prevention can restrict localStorage in cross-origin contexts
- User has disabled local storage - rare but possible via browser settings
All localStorage operations are wrapped in try-catch blocks. If a write fails, the tool continues working normally. If a read fails, the tool starts with an empty state. The user never sees an error about storage - the draft feature is an enhancement, not a dependency.
What Gets Persisted
Different tools persist different state based on what is most useful to restore:
| Tool | Persisted State |
|---|---|
| JSON Formatter | Input text |
| Base64 | Input text, encode/decode mode |
| URL Encoder | Input text, encode/decode mode |
| Diff Checker | Left input, right input |
| Hash Generator | Input text, selected algorithm |
| Regex Tester | Pattern, flags, test string |
| JWT Decoder | JWT token input |
| Case Converter | Input text, selected case |
| XML Formatter | Input text |
| YAML/JSON Converter | Input text, conversion direction |
For tools with multiple inputs (like the Diff Checker), both sides are persisted independently. For tools with mode selections (like Base64 encode/decode), the mode is persisted alongside the content.
Storage Limits and Large Documents
The 5-10MB localStorage limit is generous for most use cases. A JSON file with 50,000 lines of typical data is usually under 2MB. However, if you paste a very large document and then paste another large document in a different tool, you could theoretically hit the limit.
Our implementation handles this gracefully: if the write fails due to quota, the draft feature degrades silently. The tool still works; it just does not persist to localStorage for that particular write.
Privacy Considerations
All draft data is stored locally in your browser. It never leaves your device. You can inspect it in your browser's DevTools under Application > Local Storage. You can clear all ToolBox drafts at once by filtering for toolbox: keys and deleting them, or by clearing site data for toolbox-kit.com.
No draft data is sent to any server. This is consistent with ToolBox's privacy-first architecture where all processing happens client-side.
---
Consistent Copy Feedback
Every Copy button across all tools now shows a consistent "Copied!" feedback state for 2 seconds. This uses the shared useCopyFeedback hook to ensure uniform behavior everywhere.
Why This Matters
Before this change, some tools showed "Copied!" for 1 second, some for 3 seconds, and some did not show any feedback at all. When you copy encrypted output from the AES Encryption tool, you want to know the copy succeeded before you switch to another app and paste.
Inconsistent feedback creates doubt. "Did that actually copy? Let me click it again just to be sure." Double-copying is harmless but it is a small moment of uncertainty that adds up across a day of heavy tool use.
The useCopyFeedback Hook
import { useState, useCallback } from 'react';
function useCopyFeedback(duration: number = 2000) {
const [copied, setCopied] = useState(false);
const copy = useCallback(async (text: string) => {
try {
await navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), duration);
} catch {
// Fallback for older browsers or restricted contexts
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
setCopied(true);
setTimeout(() => setCopied(false), duration);
}
}, [duration]);
return { copied, copy };
}
// Usage in any tool
function CopyButton({ text }: { text: string }) {
const { copied, copy } = useCopyFeedback(2000);
return (
<button onClick={() => copy(text)}>
{copied ? 'Copied!' : 'Copy'}
</button>
);
}The hook uses the modern Clipboard API (navigator.clipboard.writeText) with a fallback to execCommand('copy') for older browsers. The 2-second feedback window is long enough to register visually but short enough that the button returns to its normal state before you need to copy again.
Visual Feedback Design
The "Copied!" state changes both the button text and its color from the default button style to a green confirmation color. This provides two signals simultaneously - text and color - so even users with color vision deficiencies can rely on the text change alone.
The animation is a simple CSS transition on the background-color and color properties, timed to match the 2-second feedback window.
---
Tools Upgraded in This Release
Here is the complete list of the 20 tools that received all four improvements in this release:
Formatters
| Tool | Auto-Convert Strategy | Key Use Case |
|---|---|---|
| JSON Formatter | Paste detection | Pretty-printing API responses |
| XML Formatter | Paste detection | Formatting SOAP payloads |
| Code Formatter | Ctrl+Enter only | Multi-language code formatting |
Encoders and Decoders
| Tool | Auto-Convert Strategy | Key Use Case |
|---|---|---|
| Base64 | 300ms debounce | Encoding binary data, decoding email attachments |
| URL Encoder | 300ms debounce | Building query strings, encoding redirect URLs |
| HTML Entity Encoder | 300ms debounce | Safely embedding user content in HTML |
| Hash Generator | 300ms debounce | Checksums, password verification testing |
Text Converters
| Tool | Auto-Convert Strategy | Key Use Case |
|---|---|---|
| Case Converter | 300ms debounce | Converting identifiers between naming conventions |
| Number Base Converter | 300ms debounce | Converting between decimal, hex, binary, octal |
| Text to Binary | 300ms debounce | Educational and encoding use cases |
| Morse Code | 300ms debounce | Fun and accessibility experiments |
| Color Converter | 300ms debounce | Converting between HEX, RGB, HSL |
Structured Data Converters
| Tool | Auto-Convert Strategy | Key Use Case |
|---|---|---|
| YAML/JSON Converter | Paste detection | Converting config files |
| CSV to JSON | Paste detection | Importing spreadsheet data |
| JSON to CSV | Paste detection | Exporting API data to spreadsheets |
| XML to JSON | Paste detection | Working with legacy XML APIs |
| JSON to TypeScript | Paste detection | Generating type definitions from API responses |
Developer Tools
| Tool | Auto-Convert Strategy | Key Use Case |
|---|---|---|
| JWT Decoder | Paste detection | Inspecting authentication tokens |
| Regex Tester | 300ms debounce | Building and debugging regular expressions |
| Diff Checker | Ctrl+Enter only | Comparing text versions |
---
Real-World Workflow Examples
These improvements combine to create meaningfully faster workflows. Here are a few concrete examples.
Workflow 1: Debugging an API Response
You are building a feature and the API is returning unexpected data. You copy the raw JSON response from your browser's network tab.
With the old experience:
- Open JSON Formatter
- Ctrl+V to paste
- Click Format
- Read the output
With the new experience:
- Open JSON Formatter
- Ctrl+V to paste - output appears immediately
- Read the output
One less click, but more importantly, no context switch. Your eyes stay on the output area instead of jumping to the Format button.
Workflow 2: Building a Regex Pattern
You are writing a regex to validate email addresses. You need to iterate on the pattern multiple times.
With the new Regex Tester:
- Open the tool (your previous test string is already there from persistent drafts)
- Type your pattern - results update as you type with 300ms debounce
- Refine the pattern by editing in place
- When you have the right pattern, Ctrl+A Ctrl+C to copy it
The persistent draft means you never lose your test string between sessions. The 300ms debounce means each refinement shows immediate feedback.
Workflow 3: Preparing Data for a Database Import
You have a CSV export from your analytics platform and need to import it into a database that expects JSON.
- Open CSV to JSON
- Paste the CSV - it auto-converts immediately
- Close the tab (someone messages you urgently)
- Open CSV to JSON again - your CSV is still there
- Click Copy to get the JSON output
The persistent draft saves the session even though you had to drop everything.
Workflow 4: Encoding Multiple URLs
You are building a campaign tracking system and need to encode a series of redirect URLs.
- Open URL Encoder
- Paste the first URL - encoded output appears immediately
- Replace it with the second URL - output updates automatically
- Replace it with the third URL - output updates automatically
No clicking at all. The tool behaves like a live preview, not a click-to-run converter.
---
The Technical Architecture Behind Auto-Convert
For developers curious about the implementation details, here is how these features work at a system level.
Component Structure
Each upgraded tool follows this pattern:
'use client';
import { useState, useEffect, useRef } from 'react';
import { useLocalStorageDraft } from '@/hooks/useLocalStorageDraft';
import { useCtrlEnter } from '@/hooks/useCtrlEnter';
import { useCopyFeedback } from '@/hooks/useCopyFeedback';
import { useDebounce } from '@/hooks/useDebounce';
export function ToolClient() {
// Persistent draft
const [input, setInput] = useLocalStorageDraft('tool-name:input');
const [output, setOutput] = useState('');
const [error, setError] = useState('');
// Auto-convert with debounce
const debouncedInput = useDebounce(input, 300);
useEffect(() => {
if (debouncedInput) {
try {
setOutput(convert(debouncedInput));
setError('');
} catch (err) {
setError(String(err));
}
}
}, [debouncedInput]);
// Keyboard shortcut
const { onKeyDown } = useCtrlEnter(() => {
setOutput(convert(input));
});
// Copy feedback
const { copied, copy } = useCopyFeedback();
return (
<div>
<textarea
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={onKeyDown}
/>
<button onClick={() => copy(output)}>
{copied ? 'Copied!' : 'Copy'}
</button>
<textarea value={output} readOnly />
</div>
);
}All four features are implemented as composable hooks. This means adding them to a new tool is a matter of importing and using four hooks - no copy-pasting of business logic.
Performance Characteristics
| Feature | Performance Impact |
|---|---|
| Auto-convert debounce | ~0ms at idle, ~1ms per conversion |
| Paste detection | ~0ms overhead on each keypress |
| localStorage reads | ~1ms on component mount (lazy init) |
| localStorage writes | ~2ms per write, debounced to every 500ms |
| Copy feedback | ~0ms (async clipboard API) |
These numbers are negligible. The debounce and paste detection actually improve perceived performance compared to the click-to-convert model by eliminating the click latency entirely.
---
Browser Compatibility
All four features work in all modern browsers. Here is the compatibility picture:
| Feature | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| Clipboard API | 66+ | 63+ | 13.1+ | 79+ |
| localStorage | All | All | All | All |
| KeyboardEvent.key | All | All | All | All |
| Paste event detection | All | All | All | All |
The copy functionality falls back to execCommand('copy') for any browser that does not support the modern Clipboard API. In practice, this covers browsers older than 2019, which represent less than 1% of developer traffic.
---
What Is Coming Next
These four improvements are a foundation, not a ceiling. Here is what we are thinking about building on top of this:
- Output draft persistence - save the formatted output, not just the input, so you can return to a completed format job
- Multiple draft slots - store up to five previous inputs per tool, like a mini history
- Cross-tab sync - use the
storageevent to sync drafts between tabs of the same tool in real time - Export to Gist - one-click export of any tool's input or output to a GitHub Gist
---
Try It Now
The best way to experience these improvements is to use the tools on your next real task.
Start with the JSON Formatter - paste any JSON object and watch it format instantly without clicking anything. If you close the tab and come back, your JSON will be waiting for you.
Then try the Diff Checker with Ctrl+Enter to compare two text blocks entirely from the keyboard.
For an encoding workflow, open the Base64 tool and start typing. Every character you add updates the output in real time.
These are small changes, but they add up to a meaningfully faster experience for the kinds of repetitive tasks developers do dozens of times every day.
Related Tools
Free, private, no signup required
You might also like
8 min read
Best Free Unix Timestamp Converters Compared - Epochconverter Alternatives
10 min read
Best Free JWT Decoders Compared - jwt.io Alternatives Worth Knowing
9 min read
Best Free Cron Expression Generators - Crontab.guru Alternatives Compared
Want higher limits, batch processing, and AI tools?