1

I have a child component in Vue.js that gets passed a prop from the parent component. While the child component can read the prop, it can not write to the prop without throwing a TypeScript read-only error. How do I let the child component write to the prop?

Here is an example parent component:

<template>
    <ChildComponent :count="parentCount"></ChildComponent>
</template>

<script setup lang="ts">
    const parentCount = ref(0);
</script>

And an example child component:

<template>
    <h1>{{ count }}</h1>
</template>

<script setup lang="ts">
    defineProps({
        count: { type: Number, required: true }
    });
</script>
2
  • 1
    Maybe would be better to use a store in your case Commented Feb 12 at 20:46
  • 1
    @FernandoDelCantão What do you mean by a store? Commented Feb 12 at 20:48

2 Answers 2

2

You could use a store to manage the state of your application in a more elegant and easier way, Nuxt 3 has a fairly simple integration with Pinia, here is a link to that. @pinia/nuxt

A store is a global state that can be accessed and updated anywhere without having to pass props from the parent component to the child components, this is specially useful if you have deeply nested child components.

Please take a look at the Pinia documentation for more information but this probably fulfills your use case. Here's a simple example taken from Pinia docs.

// Install @pinia/nuxt and add it as a module in your nuxt.config.js 
export default defineNuxtConfig({
    modules: ['@pinia/nuxt'],
})
// stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => {
    return { count: 0 }
  },
  // could also be defined as
  // state: () => ({ count: 0 })
  actions: {
    increment() {
      this.count++
    },
  },
})
// Your component
<script setup>
import { useCounterStore } from '@/stores/counter'

const counter = useCounterStore()

counter.count++
// with autocompletion ✨
counter.$patch({ count: counter.count + 1 })
// or using an action instead
counter.increment()
</script>

<template>
  <!-- Access the state directly from the store -->
  <div>Current Count: {{ counter.count }}</div>
</template>
Sign up to request clarification or add additional context in comments.

1 Comment

This solved the issue, thank you!
0

Feels like over-complicating to use a store just for this case. You can simply use v-model to create two-way bindings.

Docs: https://vuejs.org/guide/components/v-model.html

<!-- ParentComponent.vue -->

<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'

const counter = ref(0)
</script>

<template>
  in parent: {{ counter }}
  <br />
  <ChildComponent v-model:counter="counter" />
</template>
<!-- ChildComponent.vue -->

<script setup lang="ts">
const counter = defineModel<number>('counter', { required: true })
</script>

<template>
  in child: {{ counter }}
  <br />
  <button @click="counter++">
    increase counter
  </button>
</template>

Playground

1 Comment

I ended up needing the values accessible across the whole app, so a store worked better for me. For this question, however, this would have worked fine. Thanks for taking the time to help!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.