Files
stack/packages/mosaic/framework/skills/vueuse-functions/references/syncRef.md
T
fargo 1a822493ba format: apply repo prettier (3.8.1) to the folded skills tree
963 markdown files reformatted with the repository's pinned prettier so
pnpm format:check covers the folded tree like every other repo file.

The formatter's embedded-language pass also normalized code fences
(TS semicolons, closed HTML tags in examples, lowercased CSS hex colors,
one renumbered list that skipped an index). Alphanumeric token deltas vs
the fold commit were audited file-by-file; all are formatter-equivalent
markup normalizations plus the four sanitized skills.
2026-08-19 14:37:17 -05:00

3.9 KiB

category, related
category related
Reactivity syncRefs

syncRef

Two-way refs synchronization.

Usage

import { syncRef } from '@vueuse/core';

const a = ref('a');
const b = ref('b');

const stop = syncRef(a, b);

console.log(a.value); // a

b.value = 'foo';

console.log(a.value); // foo

a.value = 'bar';

console.log(b.value); // bar

One directional

import { syncRef } from '@vueuse/core';

const a = ref('a');
const b = ref('b');

const stop = syncRef(a, b, { direction: 'rtl' });

Custom Transform

import { syncRef } from '@vueuse/core';

const a = ref(10);
const b = ref(2);

const stop = syncRef(a, b, {
  transform: {
    ltr: (left) => left * 2,
    rtl: (right) => right / 2,
  },
});

console.log(b.value); // 20

b.value = 30;

console.log(a.value); // 15

Type Declarations

type Direction = 'ltr' | 'rtl' | 'both';
type SpecificFieldPartial<T, K extends keyof T> = Partial<Pick<T, K>> & Omit<T, K>;
/**
 * A = B
 */
type Equal<A, B> = [A] extends [B] ? ([B] extends [A] ? true : false) : false;
/**
 * A ∩ B ≠ ∅
 */
type IntersectButNotEqual<A, B> =
  Equal<A, B> extends true ? false : A & B extends never ? false : true;
/**
 * A ⊆ B
 */
type IncludeButNotEqual<A, B> = Equal<A, B> extends true ? false : A extends B ? true : false;
/**
 * A ∩ B = ∅
 */
type NotIntersect<A, B> = Equal<A, B> extends true ? false : A & B extends never ? true : false;
interface EqualType<
  D extends Direction,
  L,
  R,
  O extends keyof Transform<L, R> = D extends 'both' ? 'ltr' | 'rtl' : D,
> {
  transform?: SpecificFieldPartial<Pick<Transform<L, R>, O>, O>;
}
type StrictIncludeMap<
  IncludeType extends 'LR' | 'RL',
  D extends Exclude<Direction, 'both'>,
  L,
  R,
> = Equal<[IncludeType, D], ['LR', 'ltr']> & Equal<[IncludeType, D], ['RL', 'rtl']> extends true
  ? {
      transform?: SpecificFieldPartial<Pick<Transform<L, R>, D>, D>;
    }
  : {
      transform: Pick<Transform<L, R>, D>;
    };
type StrictIncludeType<
  IncludeType extends 'LR' | 'RL',
  D extends Direction,
  L,
  R,
> = D extends 'both'
  ? {
      transform: SpecificFieldPartial<Transform<L, R>, IncludeType extends 'LR' ? 'ltr' : 'rtl'>;
    }
  : D extends Exclude<Direction, 'both'>
    ? StrictIncludeMap<IncludeType, D, L, R>
    : never;
type IntersectButNotEqualType<D extends Direction, L, R> = D extends 'both'
  ? {
      transform: Transform<L, R>;
    }
  : D extends Exclude<Direction, 'both'>
    ? {
        transform: Pick<Transform<L, R>, D>;
      }
    : never;
type NotIntersectType<D extends Direction, L, R> = IntersectButNotEqualType<D, L, R>;
interface Transform<L, R> {
  ltr: (left: L) => R;
  rtl: (right: R) => L;
}
type TransformType<D extends Direction, L, R> =
  Equal<L, R> extends true
    ? EqualType<D, L, R>
    : IncludeButNotEqual<L, R> extends true
      ? StrictIncludeType<'LR', D, L, R>
      : IncludeButNotEqual<R, L> extends true
        ? StrictIncludeType<'RL', D, L, R>
        : IntersectButNotEqual<L, R> extends true
          ? IntersectButNotEqualType<D, L, R>
          : NotIntersect<L, R> extends true
            ? NotIntersectType<D, L, R>
            : never;
export type SyncRefOptions<L, R, D extends Direction> = ConfigurableFlushSync & {
  /**
   * Watch deeply
   *
   * @default false
   */
  deep?: boolean;
  /**
   * Sync values immediately
   *
   * @default true
   */
  immediate?: boolean;
  /**
   * Direction of syncing. Value will be redefined if you define syncConvertors
   *
   * @default 'both'
   */
  direction?: D;
} & TransformType<D, L, R>;
/**
 * Two-way refs synchronization.
 * From the set theory perspective to restrict the option's type
 * Check in the following order:
 * 1. L = R
 * 2. L ∩ R ≠ ∅
 * 3. L ⊆ R
 * 4. L ∩ R = ∅
 */
export declare function syncRef<L, R, D extends Direction = 'both'>(
  left: Ref<L>,
  right: Ref<R>,
  ...[options]: Equal<L, R> extends true
    ? [options?: SyncRefOptions<L, R, D>]
    : [options: SyncRefOptions<L, R, D>]
): () => void;