블로그 목록
프론트엔드

CSS field-sizing로 textarea 자동 크기 조절하기 — JS 없이 내용 따라 늘어나게

채팅 입력창 만들다가 또 이 문제를 만났다. textarea에 줄이 늘어나면 입력창도 같이 커져야 하는데, 기본 textarea는 고정 높이라 스크롤이 생긴다. 슬랙이나 카톡처럼 내용 따라 부드럽게 늘어나게 하고 싶은데, 순수 CSS로는 안 되던 영역이었다.

지금까지는 JS로 해결했다

전통적인 방법은 input 이벤트마다 scrollHeight를 재서 높이를 다시 잡아주는 거다. 대충 이런 코드를 매번 짰다.

function autoResize(el) {
  el.style.height = 'auto';           // 먼저 줄였다가
  el.style.height = el.scrollHeight + 'px'; // 실제 높이로 다시 세팅
}

textarea.addEventListener('input', (e) => autoResize(e.target));

동작은 하는데 성가시다. height: auto로 리셋했다가 다시 재는 과정에서 리플로우가 두 번 일어나고, React에서는 useRefuseEffect까지 물려야 한다. react-textarea-autosize 같은 라이브러리를 그냥 설치해버린 적도 많았다.

field-sizing: content

이걸 CSS 한 줄이 대체한다.

textarea {
  field-sizing: content;
  min-height: 3rem;
  max-height: 12rem; /* 이 이상은 스크롤 */
}

field-sizing: content를 주면 폼 컨트롤이 고정 크기가 아니라 내용 크기에 맞춰 커지고 줄어든다. 타이핑하면 textarea가 실시간으로 늘어나고, 지우면 다시 줄어든다. 이벤트 리스너도, ref도, 리플로우 걱정도 없다.

min-heightmax-height를 같이 잡아주는 게 실전 포인트다. 빈 상태에서 너무 납작해지지 않게 최소 높이를 주고, 무한정 늘어나지 않게 최대 높이를 정하면 그 지점부터는 알아서 스크롤이 생긴다.

textarea만 되는 게 아니다

select에도 먹힌다. 기본 select는 가장 긴 옵션 기준으로 너비가 고정되는데, field-sizing: content를 주면 현재 선택된 옵션 길이에 맞춰 너비가 딱 붙는다.

select {
  field-sizing: content;
}

짧은 옵션을 고르면 셀렉트 박스도 같이 짧아진다. input[type="text"]도 마찬가지로 입력한 글자 수만큼만 너비를 차지하게 만들 수 있다.

브라우저 지원

Chrome 123+, Edge, 그리고 최근 Firefox와 Safari도 지원이 들어왔다. 아직 안 되는 브라우저를 위한 폴백은 크게 걱정 안 해도 된다. 지원 안 하면 그냥 기존 고정 크기 textarea로 동작할 뿐, 깨지지 않으니까 점진적 향상(progressive enhancement)으로 그냥 얹으면 된다.

@supports (field-sizing: content) {
  textarea { field-sizing: content; }
}

채팅창, 댓글 입력, 태그 입력 같은 데서 textarea 자동 리사이즈 로직을 매번 짜거나 라이브러리를 붙였다면, 이제 CSS 한 줄로 끝난다. 라이브러리 하나 지웠다.