diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap
index 6c358654..792de768 100644
--- a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap
+++ b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap
@@ -554,212 +554,6 @@ return () => {}
 }"
 `;
 
-exports[`SFC compile 
-      `)
+      `,
+        { refSugar: true }
+      )
       assertCode(content)
       expect(content).toMatch(`import { ref } from 'vue'`)
     })
@@ -829,7 +832,9 @@ const emit = defineEmits(['a', 'b'])
       expected: string | ((content: string) => boolean),
       shouldAsync = true
     ) {
-      const { content } = compile(``)
+      const { content } = compile(``, {
+        refSugar: true
+      })
       expect(content).toMatch(`${shouldAsync ? `async ` : ``}setup(`)
       if (typeof expected === 'string') {
         expect(content).toMatch(expected)
@@ -893,238 +898,6 @@ const emit = defineEmits(['a', 'b'])
     })
   })
 
-  describe('ref: syntax sugar', () => {
-    test('convert ref declarations', () => {
-      const { content, bindings } = compile(``)
-      expect(content).toMatch(`import { ref as _ref } from 'vue'`)
-      expect(content).not.toMatch(`ref: foo`)
-      expect(content).not.toMatch(`ref: a`)
-      expect(content).not.toMatch(`ref: b`)
-      expect(content).toMatch(`const foo = _ref()`)
-      expect(content).toMatch(`const a = _ref(1)`)
-      expect(content).toMatch(`
-      const b = _ref({
-        count: 0
-      })
-      `)
-      // normal declarations left untouched
-      expect(content).toMatch(`let c = () => {}`)
-      expect(content).toMatch(`let d`)
-      assertCode(content)
-      expect(bindings).toStrictEqual({
-        foo: BindingTypes.SETUP_REF,
-        a: BindingTypes.SETUP_REF,
-        b: BindingTypes.SETUP_REF,
-        c: BindingTypes.SETUP_LET,
-        d: BindingTypes.SETUP_LET
-      })
-    })
-
-    test('multi ref declarations', () => {
-      const { content, bindings } = compile(``)
-      expect(content).toMatch(`
-      const a = _ref(1), b = _ref(2), c = _ref({
-        count: 0
-      })
-      `)
-      expect(content).toMatch(`return { a, b, c }`)
-      assertCode(content)
-      expect(bindings).toStrictEqual({
-        a: BindingTypes.SETUP_REF,
-        b: BindingTypes.SETUP_REF,
-        c: BindingTypes.SETUP_REF
-      })
-    })
-
-    test('should not convert non ref labels', () => {
-      const { content } = compile(``)
-      expect(content).toMatch(`foo: a = 1, b = 2`)
-      assertCode(content)
-    })
-
-    test('accessing ref binding', () => {
-      const { content } = compile(``)
-      expect(content).toMatch(`console.log(a.value)`)
-      expect(content).toMatch(`return a.value + 1`)
-      assertCode(content)
-    })
-
-    test('cases that should not append .value', () => {
-      const { content } = compile(``)
-      expect(content).not.toMatch(`a.value`)
-    })
-
-    test('mutating ref binding', () => {
-      const { content } = compile(``)
-      expect(content).toMatch(`a.value++`)
-      expect(content).toMatch(`a.value = a.value + 1`)
-      expect(content).toMatch(`b.value.count++`)
-      expect(content).toMatch(`b.value.count = b.value.count + 1`)
-      expect(content).toMatch(`;({ a: a.value } = { a: 2 })`)
-      expect(content).toMatch(`;[a.value] = [1]`)
-      assertCode(content)
-    })
-
-    test('using ref binding in property shorthand', () => {
-      const { content } = compile(``)
-      expect(content).toMatch(`const b = { a: a.value }`)
-      // should not convert destructure
-      expect(content).toMatch(`const { a } = b`)
-      assertCode(content)
-    })
-
-    test('should not rewrite scope variable', () => {
-      const { content } = compile(`
-      `)
-      expect(content).toMatch('console.log(a)')
-      expect(content).toMatch('console.log(b.value)')
-      expect(content).toMatch('console.log(c)')
-      expect(content).toMatch('console.log($d)')
-      expect(content).toMatch('console.log(d.value)')
-      expect(content).toMatch('console.log(e)')
-      assertCode(content)
-    })
-
-    test('object destructure', () => {
-      const { content, bindings } = compile(``)
-      expect(content).toMatch(
-        `const n = _ref(1), { a: __a, b: __c, d: __d = 1, e: __f = 2, ...__g } = useFoo()`
-      )
-      expect(content).toMatch(`const { foo: __foo } = useSomthing(() => 1)`)
-      expect(content).toMatch(`\nconst a = _ref(__a);`)
-      expect(content).not.toMatch(`\nconst b = _ref(__b);`)
-      expect(content).toMatch(`\nconst c = _ref(__c);`)
-      expect(content).toMatch(`\nconst d = _ref(__d);`)
-      expect(content).not.toMatch(`\nconst e = _ref(__e);`)
-      expect(content).toMatch(`\nconst f = _ref(__f);`)
-      expect(content).toMatch(`\nconst g = _ref(__g);`)
-      expect(content).toMatch(`\nconst foo = _ref(__foo);`)
-      expect(content).toMatch(
-        `console.log(n.value, a.value, c.value, d.value, f.value, g.value, foo.value)`
-      )
-      expect(content).toMatch(`return { n, a, c, d, f, g, foo }`)
-      expect(bindings).toStrictEqual({
-        n: BindingTypes.SETUP_REF,
-        a: BindingTypes.SETUP_REF,
-        c: BindingTypes.SETUP_REF,
-        d: BindingTypes.SETUP_REF,
-        f: BindingTypes.SETUP_REF,
-        g: BindingTypes.SETUP_REF,
-        foo: BindingTypes.SETUP_REF
-      })
-      assertCode(content)
-    })
-
-    test('array destructure', () => {
-      const { content, bindings } = compile(``)
-      expect(content).toMatch(
-        `const n = _ref(1), [__a, __b = 1, ...__c] = useFoo()`
-      )
-      expect(content).toMatch(`\nconst a = _ref(__a);`)
-      expect(content).toMatch(`\nconst b = _ref(__b);`)
-      expect(content).toMatch(`\nconst c = _ref(__c);`)
-      expect(content).toMatch(`console.log(n.value, a.value, b.value, c.value)`)
-      expect(content).toMatch(`return { n, a, b, c }`)
-      expect(bindings).toStrictEqual({
-        n: BindingTypes.SETUP_REF,
-        a: BindingTypes.SETUP_REF,
-        b: BindingTypes.SETUP_REF,
-        c: BindingTypes.SETUP_REF
-      })
-      assertCode(content)
-    })
-
-    test('nested destructure', () => {
-      const { content, bindings } = compile(``)
-      expect(content).toMatch(`const [{ a: { b: __b }}] = useFoo()`)
-      expect(content).toMatch(`const { c: [__d, __e] } = useBar()`)
-      expect(content).not.toMatch(`\nconst a = _ref(__a);`)
-      expect(content).not.toMatch(`\nconst c = _ref(__c);`)
-      expect(content).toMatch(`\nconst b = _ref(__b);`)
-      expect(content).toMatch(`\nconst d = _ref(__d);`)
-      expect(content).toMatch(`\nconst e = _ref(__e);`)
-      expect(content).toMatch(`return { b, d, e }`)
-      expect(bindings).toStrictEqual({
-        b: BindingTypes.SETUP_REF,
-        d: BindingTypes.SETUP_REF,
-        e: BindingTypes.SETUP_REF
-      })
-      assertCode(content)
-    })
-  })
-
   describe('errors', () => {
     test('`)
-      ).toThrow(`ref: statements can only contain assignment expressions`)
-    })
-
     test('defineProps/Emit() w/ both type and non-type args', () => {
       expect(() => {
         compile(``)
-      ).toThrow(`cannot reference locally declared variables`)
-
-      expect(() =>
-        compile(``)
-      ).toThrow(`cannot reference locally declared variables`)
-    })
-
     test('should allow defineProps/Emit() referencing scope var', () => {
       assertCode(
         compile(``)
+    expect(content).toMatch(`import { ref as _ref } from 'vue'`)
+    expect(content).not.toMatch(`ref: foo`)
+    expect(content).not.toMatch(`ref: a`)
+    expect(content).not.toMatch(`ref: b`)
+    expect(content).toMatch(`const foo = _ref()`)
+    expect(content).toMatch(`const a = _ref(1)`)
+    expect(content).toMatch(`
+    const b = _ref({
+      count: 0
+    })
+    `)
+    // normal declarations left untouched
+    expect(content).toMatch(`let c = () => {}`)
+    expect(content).toMatch(`let d`)
+    assertCode(content)
+    expect(bindings).toStrictEqual({
+      foo: BindingTypes.SETUP_REF,
+      a: BindingTypes.SETUP_REF,
+      b: BindingTypes.SETUP_REF,
+      c: BindingTypes.SETUP_LET,
+      d: BindingTypes.SETUP_LET
+    })
+  })
+
+  test('multi ref declarations', () => {
+    const { content, bindings } = compileWithRefSugar(``)
+    expect(content).toMatch(`
+    const a = _ref(1), b = _ref(2), c = _ref({
+      count: 0
+    })
+    `)
+    expect(content).toMatch(`return { a, b, c }`)
+    assertCode(content)
+    expect(bindings).toStrictEqual({
+      a: BindingTypes.SETUP_REF,
+      b: BindingTypes.SETUP_REF,
+      c: BindingTypes.SETUP_REF
+    })
+  })
+
+  test('should not convert non ref labels', () => {
+    const { content } = compileWithRefSugar(``)
+    expect(content).toMatch(`foo: a = 1, b = 2`)
+    assertCode(content)
+  })
+
+  test('accessing ref binding', () => {
+    const { content } = compileWithRefSugar(``)
+    expect(content).toMatch(`console.log(a.value)`)
+    expect(content).toMatch(`return a.value + 1`)
+    assertCode(content)
+  })
+
+  test('cases that should not append .value', () => {
+    const { content } = compileWithRefSugar(``)
+    expect(content).not.toMatch(`a.value`)
+  })
+
+  test('mutating ref binding', () => {
+    const { content } = compileWithRefSugar(``)
+    expect(content).toMatch(`a.value++`)
+    expect(content).toMatch(`a.value = a.value + 1`)
+    expect(content).toMatch(`b.value.count++`)
+    expect(content).toMatch(`b.value.count = b.value.count + 1`)
+    expect(content).toMatch(`;({ a: a.value } = { a: 2 })`)
+    expect(content).toMatch(`;[a.value] = [1]`)
+    assertCode(content)
+  })
+
+  test('using ref binding in property shorthand', () => {
+    const { content } = compileWithRefSugar(``)
+    expect(content).toMatch(`const b = { a: a.value }`)
+    // should not convert destructure
+    expect(content).toMatch(`const { a } = b`)
+    assertCode(content)
+  })
+
+  test('should not rewrite scope variable', () => {
+    const { content } = compileWithRefSugar(`
+    `)
+    expect(content).toMatch('console.log(a)')
+    expect(content).toMatch('console.log(b.value)')
+    expect(content).toMatch('console.log(c)')
+    expect(content).toMatch('console.log($d)')
+    expect(content).toMatch('console.log(d.value)')
+    expect(content).toMatch('console.log(e)')
+    assertCode(content)
+  })
+
+  test('object destructure', () => {
+    const { content, bindings } = compileWithRefSugar(``)
+    expect(content).toMatch(
+      `const n = _ref(1), { a: __a, b: __c, d: __d = 1, e: __f = 2, ...__g } = useFoo()`
+    )
+    expect(content).toMatch(`const { foo: __foo } = useSomthing(() => 1)`)
+    expect(content).toMatch(`\nconst a = _ref(__a);`)
+    expect(content).not.toMatch(`\nconst b = _ref(__b);`)
+    expect(content).toMatch(`\nconst c = _ref(__c);`)
+    expect(content).toMatch(`\nconst d = _ref(__d);`)
+    expect(content).not.toMatch(`\nconst e = _ref(__e);`)
+    expect(content).toMatch(`\nconst f = _ref(__f);`)
+    expect(content).toMatch(`\nconst g = _ref(__g);`)
+    expect(content).toMatch(`\nconst foo = _ref(__foo);`)
+    expect(content).toMatch(
+      `console.log(n.value, a.value, c.value, d.value, f.value, g.value, foo.value)`
+    )
+    expect(content).toMatch(`return { n, a, c, d, f, g, foo }`)
+    expect(bindings).toStrictEqual({
+      n: BindingTypes.SETUP_REF,
+      a: BindingTypes.SETUP_REF,
+      c: BindingTypes.SETUP_REF,
+      d: BindingTypes.SETUP_REF,
+      f: BindingTypes.SETUP_REF,
+      g: BindingTypes.SETUP_REF,
+      foo: BindingTypes.SETUP_REF
+    })
+    assertCode(content)
+  })
+
+  test('array destructure', () => {
+    const { content, bindings } = compileWithRefSugar(``)
+    expect(content).toMatch(
+      `const n = _ref(1), [__a, __b = 1, ...__c] = useFoo()`
+    )
+    expect(content).toMatch(`\nconst a = _ref(__a);`)
+    expect(content).toMatch(`\nconst b = _ref(__b);`)
+    expect(content).toMatch(`\nconst c = _ref(__c);`)
+    expect(content).toMatch(`console.log(n.value, a.value, b.value, c.value)`)
+    expect(content).toMatch(`return { n, a, b, c }`)
+    expect(bindings).toStrictEqual({
+      n: BindingTypes.SETUP_REF,
+      a: BindingTypes.SETUP_REF,
+      b: BindingTypes.SETUP_REF,
+      c: BindingTypes.SETUP_REF
+    })
+    assertCode(content)
+  })
+
+  test('nested destructure', () => {
+    const { content, bindings } = compileWithRefSugar(``)
+    expect(content).toMatch(`const [{ a: { b: __b }}] = useFoo()`)
+    expect(content).toMatch(`const { c: [__d, __e] } = useBar()`)
+    expect(content).not.toMatch(`\nconst a = _ref(__a);`)
+    expect(content).not.toMatch(`\nconst c = _ref(__c);`)
+    expect(content).toMatch(`\nconst b = _ref(__b);`)
+    expect(content).toMatch(`\nconst d = _ref(__d);`)
+    expect(content).toMatch(`\nconst e = _ref(__e);`)
+    expect(content).toMatch(`return { b, d, e }`)
+    expect(bindings).toStrictEqual({
+      b: BindingTypes.SETUP_REF,
+      d: BindingTypes.SETUP_REF,
+      e: BindingTypes.SETUP_REF
+    })
+    assertCode(content)
+  })
+
+  describe('errors', () => {
+    test('ref: non-assignment expressions', () => {
+      expect(() =>
+        compile(
+          ``,
+          { refSugar: true }
+        )
+      ).toThrow(`ref: statements can only contain assignment expressions`)
+    })
+
+    test('defineProps/Emit() referencing ref declarations', () => {
+      expect(() =>
+        compile(
+          ``,
+          { refSugar: true }
+        )
+      ).toThrow(`cannot reference locally declared variables`)
+
+      expect(() =>
+        compile(
+          ``,
+          { refSugar: true }
+        )
+      ).toThrow(`cannot reference locally declared variables`)
+    })
+  })
+})
diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts
index 9347e996..d8873610 100644
--- a/packages/compiler-sfc/src/compileScript.ts
+++ b/packages/compiler-sfc/src/compileScript.ts
@@ -189,7 +189,7 @@ export function compileScript(
   const setupBindings: Record = Object.create(null)
   const refBindings: Record = Object.create(null)
   const refIdentifiers: Set = new Set()
-  const enableRefSugar = options.refSugar !== false
+  const enableRefSugar = !!options.refSugar
   let defaultExport: Node | undefined
   let hasDefinePropsCall = false
   let hasDefineEmitCall = false
@@ -732,10 +732,11 @@ export function compileScript(
         )
         processRefExpression(node.body.expression, node)
       } else {
-        // TODO if we end up shipping ref: sugar as an opt-in feature,
-        // need to proxy the option in vite, vue-loader and rollup-plugin-vue.
         error(
-          `ref: sugar needs to be explicitly enabled via vite or vue-loader options.`,
+          `ref: sugar now needs to be explicitly enabled via @vitejs/plugin-vue ` +
+            `or vue-loader options:\n` +
+            `- @vitejs/plugin-vue: via \`script.refSugar\`\n` +
+            `- vue-loader: via \`refSugar\` (requires 16.3.0+)`,
           node
         )
       }