Text Expander & Snippets
Expand abbreviations with built-in and custom dictionaries. Create and insert reusable text snippets.
Text
100% Free
Runs in Browser
Preview
Custom Dictionary
Built-in: 20 abbreviations • Custom: 0
Snippets
Source Code
Toggle the controls below the code to live-edit the initial state values.
text-expander.tsx
import { useState } from "react";
import { Textarea } from "@/components/ui/textarea";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Copy, Check, Plus, Trash2 } from "lucide-react";
const DEFAULT_ABBREVIATIONS: Record<string, string> = {
"btw": "by the way", "fyi": "for your information", "asap": "as soon as possible",
"imo": "in my opinion", "tbh": "to be honest", "afaik": "as far as I know",
"brb": "be right back", "eta": "estimated time of arrival", "diy": "do it yourself",
"faq": "frequently asked questions", "rsvp": "please respond", "tbd": "to be determined",
"aka": "also known as", "eg": "for example", "ie": "that is", "etc": "et cetera",
"vs": "versus", "dept": "department", "govt": "government", "approx": "approximately",
};
export default function TextExpander() {
const [input, setInput] = useState("");
const [output, setOutput] = useState("");
const [copied, setCopied] = useState(false);
const [customDict, setCustomDict] = useState<Record<string, string>>({});
const [newKey, setNewKey] = useState("");
const [newValue, setNewValue] = useState("");
const [snippets, setSnippets] = useState<{ name: string; content: string }[]>([
{ name: "greeting", content: "Dear Sir/Madam,\n\nI hope this email finds you well." },
{ name: "closing", content: "Thank you for your time and consideration.\n\nBest regards," },
]);
const [newSnippetName, setNewSnippetName] = useState("");
const [newSnippetContent, setNewSnippetContent] = useState("");
const allDict = { ...DEFAULT_ABBREVIATIONS, ...customDict };
const expand = () => {
let result = input;
for (const [abbr, expansion] of Object.entries(allDict)) {
result = result.replace(new RegExp(`\\b${abbr}\\b`, "gi"), expansion);
}
setOutput(result);
};
const addCustom = () => {
if (newKey && newValue) {
setCustomDict((prev) => ({ ...prev, [newKey.toLowerCase()]: newValue }));
setNewKey("");
setNewValue("");
}
};
const addSnippet = () => {
if (newSnippetName && newSnippetContent) {
setSnippets((prev) => [...prev, { name: newSnippetName, content: newSnippetContent }]);
setNewSnippetName("");
setNewSnippetContent("");
}
};
const insertSnippet = (content: string) => {
setInput((prev) => prev + (prev ? "\n" : "") + content);
};
const copyOutput = async () => {
await navigator.clipboard.writeText(output);
setCopied(true);
setTimeout(() => setCopied(false), 1500);
};
return (
<div className="space-y-4">
<Textarea placeholder="Type text with abbreviations (e.g., 'btw, fyi, asap')..." value={input}
onChange={(e) => setInput(e.target.value)} className="min-h-[120px] resize-y text-sm" />
<Button onClick={expand}>Expand Abbreviations</Button>
{output && (
<div className="relative rounded-lg border bg-muted/50 p-4">
<pre className="whitespace-pre-wrap text-sm">{output}</pre>
<Button variant="ghost" size="icon" className="absolute top-2 right-2" onClick={copyOutput}>
{copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
</Button>
</div>
)}
{/* Custom Dictionary */}
<div className="rounded-lg border p-4 space-y-3">
<span className="text-sm font-medium">Custom Dictionary</span>
<div className="flex gap-2">
<Input placeholder="abbr" value={newKey} onChange={(e) => setNewKey(e.target.value)} className="w-24" />
<Input placeholder="expansion" value={newValue} onChange={(e) => setNewValue(e.target.value)} className="flex-1" />
<Button size="sm" onClick={addCustom}><Plus className="h-3 w-3" /></Button>
</div>
<div className="flex flex-wrap gap-2">
{Object.entries(customDict).map(([k, v]) => (
<Badge key={k} variant="outline" className="text-xs">
{k} → {v}
<button className="ml-1 text-muted-foreground hover:text-foreground"
onClick={() => setCustomDict((prev) => { const n = { ...prev }; delete n[k]; return n; })}>×</button>
</Badge>
))}
</div>
<p className="text-xs text-muted-foreground">
Built-in: {Object.keys(DEFAULT_ABBREVIATIONS).length} abbreviations • Custom: {Object.keys(customDict).length}
</p>
</div>
{/* Snippets */}
<div className="rounded-lg border p-4 space-y-3">
<span className="text-sm font-medium">Snippets</span>
<div className="flex flex-wrap gap-2">
{snippets.map((s, i) => (
<Button key={i} variant="outline" size="sm" onClick={() => insertSnippet(s.content)}>
{s.name}
</Button>
))}
</div>
<div className="flex gap-2">
<Input placeholder="name" value={newSnippetName} onChange={(e) => setNewSnippetName(e.target.value)} className="w-24" />
<Input placeholder="content" value={newSnippetContent} onChange={(e) => setNewSnippetContent(e.target.value)} className="flex-1" />
<Button size="sm" onClick={addSnippet}><Plus className="h-3 w-3" /></Button>
</div>
</div>
</div>
);
}
Props Playground
5 props
Required Libraries
Install the external dependencies used by this tool.
bun add lucide-react react
Shadcn UI Setup
Add the required Shadcn UI primitives to your project.
bun x --bun shadcn@latest add badge button input textarea
Shadcn UI Components
The following primitives are required in your
@/components/ui
directory.
| Component | Import Path |
|---|---|
| badge | @/components/ui/badge |
| button | @/components/ui/button |
| input | @/components/ui/input |
| textarea | @/components/ui/textarea |
Imports
All import statements used in this tool.
| Source | Exports |
|---|---|
| react | useState |
| @/components/ui/textarea | Textarea |
| @/components/ui/input | Input |
| @/components/ui/button | Button |
| @/components/ui/badge | Badge |
| lucide-react | Copy, Check, Plus, Trash2 |
State Management
React state variables managed within this tool.
| Variable | Initial Value |
|---|---|
| input | "" |
| output | "" |
| copied | false |
| newKey | "" |
| newValue | "" |
| snippets | [
{ name: "greeting", content: "Dear Sir/Madam,\n\nI hope this email finds you well." },
{ name: "closing", content: "Thank you for your time and consideration.\n\nBest regards," },
] |
| newSnippetName | "" |
| newSnippetContent | "" |
Variables & Constants
Constants and computed values defined in this tool.
| Name | Value |
|---|---|
| allDict | { ...DEFAULT_ABBREVIATIONS, ...customDict } |
Functional Logic
Internal functions that handle the tool's core logic.
| Function | Parameters | Async |
|---|---|---|
| expand() | None | No |
| addCustom() | None | No |
| addSnippet() | None | No |
| insertSnippet() | content: string | No |
| copyOutput() | None |
Yes
|
External Resources
Documentation, tutorials, and package details for libraries used in this tool.