Created
February 1, 2025 20:48
-
-
Save Nusab19/a7580b6190df9b3727b3bc1e99250ec9 to your computer and use it in GitHub Desktop.
Auto-growing Textarea of ShadCN
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as React from "react"; | |
import { cn } from "@/lib/utils"; | |
const Textarea = React.forwardRef< | |
HTMLTextAreaElement, | |
React.ComponentProps<"textarea"> | |
>(({ className, ...props }, ref) => { | |
const textareaRef = React.useRef<HTMLTextAreaElement | null>(null); | |
const calculateHeight = React.useCallback(() => { | |
const textarea = textareaRef.current; | |
if (!textarea) return; | |
// Store the current scroll position | |
const scrollPos = textarea.scrollTop; | |
// Reset height to auto to get the correct scrollHeight | |
textarea.style.height = "auto"; | |
// Get the scroll height and add a small padding | |
const scrollHeight = textarea.scrollHeight; | |
// Set the new height | |
textarea.style.height = `${scrollHeight}px`; | |
// Restore the scroll position | |
textarea.scrollTop = scrollPos; | |
}, []); | |
React.useEffect(() => { | |
calculateHeight(); | |
// Add resize observer to handle window/container resizing | |
const resizeObserver = new ResizeObserver(calculateHeight); | |
if (textareaRef.current) { | |
resizeObserver.observe(textareaRef.current); | |
} | |
return () => resizeObserver.disconnect(); | |
}, [calculateHeight, props.value]); | |
const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => { | |
calculateHeight(); | |
if (props.onChange) { | |
props.onChange(e); | |
} | |
}; | |
return ( | |
<textarea | |
className={cn( | |
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", | |
"text-xs transition-all duration-100 md:text-sm", | |
className, | |
"overflow-y-hidden", | |
)} | |
ref={(element) => { | |
// Handle both forwardRef and internal ref | |
textareaRef.current = element; | |
if (typeof ref === "function") { | |
ref(element); | |
} else if (ref) { | |
ref.current = element; | |
} | |
}} | |
onChange={handleInput} | |
{...props} | |
/> | |
); | |
}); | |
Textarea.displayName = "Textarea"; | |
export { Textarea }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment