Skip to content

Instantly share code, notes, and snippets.

@Nusab19
Created February 1, 2025 20:48
Show Gist options
  • Save Nusab19/a7580b6190df9b3727b3bc1e99250ec9 to your computer and use it in GitHub Desktop.
Save Nusab19/a7580b6190df9b3727b3bc1e99250ec9 to your computer and use it in GitHub Desktop.
Auto-growing Textarea of ShadCN
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