loading...
Cover image for Creating an auto resizing textarea with Vue Composition API in 4 easy steps. I can't wait for Vue v3

Creating an auto resizing textarea with Vue Composition API in 4 easy steps. I can't wait for Vue v3

sduduzog profile image Beautus S Gumede Updated on ・2 min read

I'm assuming you now know how to add @vue/composition-api to your project so I won't waste any more time.

Step 1

Where ever you need to, in your template, just add the textarea element like so:

<template>
    <!-- anything else you probably had -->
    <textarea ref="myTextArea" v-model="content"></textarea>
</template>

Notice we added a ref attribute. name it whatever you like. Don't forget the v-model.

Step 2

Create the composition function. I created mine inside the same component file I created the textarea in. You can even create a separate file for it if you like.

function useTextArea() {
  const myTextArea = ref<HTMLTextAreaElement>([]); // remove <...> if JS
  const content = ref("");

  return { myTextArea, content };
}

If not done automatically by your editor, add the following import statement

import { createComponent, ref, watch } from "@vue/composition-api";
// remember 'createComponent' is not needed/used in javascript

Step 3

Expose these properties to your Vue template. We do that in the setup function's return statement

export default createComponent({
  // ... other things you did.
  setup(props, context) {
    // ... some other stuff
    const { myTextArea, content } = useTextArea();
    // ... maybe some more stuff, I don't know
    return {
      // ... maybe other things are here too, your things
      myTextArea,
      content
    };
  }
}) as VueConstructor;

I usually add as VueContructor just to stop the noise from eslint and vue-router complaining about the component's expected type. Not really a requirement.

Ok everything is neat now, there should be no errors but nothing works yet. We do however have a reference without having to use this.$refs bacause obviously we don't have access to this in our setup function. But through the magic of the composition api, the concept of refs is unified.

Step 4

Making it work. We go back to the useTextArea function to add a couple of lines to watch for changes in the textarea's content and calculate the element's new height after that.

function useTextArea(){
  // ... after variable declarations
  watch(() => {
    if (content.value.length === 0) return;
    myTextArea.value.rows = 2; // depends on what you want initially
    const styles = window.getComputedStyle(myTextArea.value);
    const paddingTop = parseInt(styles.paddingTop);
    const paddingBottom = parseInt(styles.paddingBottom);
    const padding = paddingTop + paddingBottom;
    const currentHeight = parseInt(styles.height) - padding;
    const initialHeight =
      (parseInt(styles.height) - padding) / myTextArea.value.rows;
    const scrollHeight = myTextArea.value.scrollHeight - padding;
    const newRows = Math.ceil(scrollHeight / initialHeight);
    textArea.value.rows = newRows;
  });
  // ... before the return statement
}

Aaand done. You now have a textarea that adds or removes rows if there are any changes with it's value. You don't have to know off hand, the height of your element or the font size of the content, which can change anytime.

One concern I have is just the performance of the code inside the watch function. If you have a better implementation please link it in the comments, I'd love to see different ideas around this. I've never enjoyed writing VueJS like this ever before.

Discussion

pic
Editor guide