Home

How to Set Up Vite React Signal

Kristopher Nathanael
2.5 minutes

How to Set Up Vite React Signal

Repo

github.com/KrisNathan/vite-react-signal-hono

Why

Signals offer a way for developers to manage state in the most efficient way possible with ease. Signals are able to offer efficiency by only rerendering what needs to be rerendered unlike legacy react useContext. Such ease is made possible by the excellent ergonomics provided by the automatic state binding and dependency tracking.

Vite + React Setup

Skip this if you already have a vite+react set up.

npm create vite@latest
  • Select React
  • Select Typescript

Steps

Here’s how you can set up vite + react with preact signals.

Install the dependencies

npm i @preact/signals-react
npm i -D @preact/signals-react-transform

At the time of writing here are the versions of those libraries:

Next, we need to configure babel

// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: [["module:@preact/signals-react-transform"]], // +
      },
    }),
  ],
});

Usage

We can declare and initialize our state this way:

// src/signals/counter.ts
import { signal } from "@preact/signals-react";

export const counter = signal(0);

To update the state we can simply just modify the .value property. Here’s an example in the form of counter button.

// src/CounterBtn.tsx
import React from "react";
import { counter } from "./signals/counter";

interface CounterBtnProps {
  children: React.ReactNode;
}

export default function CounterBtn({ children }: CounterBtnProps) {
  return (
    <button
      onClick={() => {
        counter.value++;
      }}
    >
      {children}
    </button>
  );
}

To render the state values we can just directly access .value

// src/Label.tsx
import { counter } from "./signals/counter";

export default function Label() {
  return <p>count is {counter.value}</p>;
}

Caveats

At the time of writing signals-react doesn’t support passing signals as props.

Such usage isn’t recommended:

import { counter } from "./signals/counter";

export default function Page() {
  return <SomeComponent count={counter.value} />;
}

This can be a problem if you use a component library where you can’t modify it directly and import the signal directly.

Workaround

However, theres a neat workaround that can be done.

wrappers

Pretend that the library contains the following:

// src/Label.tsx
interface LabelProps {
  count: number;
}

// simulate external component library that only accepts prop
// we can't directly import the signal
export default function Label({ count }: LabelProps) {
  return <p>count is {count}</p>;
}

We can simply wrap it around another component.

// src/LabelWrapper.tsx
import Label from "./Label";
import { counter } from "./signals/counter";

// simulate external component library that only accepts prop
// we can't directly import the signal
const LabelWrapper = () => <Label count={counter.value} />;
export default LabelWrapper;