DEV Community

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

Posted on • Edited on

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

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>
Enter fullscreen mode Exit fullscreen mode

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 };
}

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)