💎 of solid-primitives, part 1: context
Tags: solidjs, typescript, javascript, webdev
Published: 2024-02-05 10:23:27

[Solid.js](https://solidjs.com) is already pretty powerful, but even so, there are things it cannot do out of the box. Here's where the community comes in and provides packages to enhance your development experience: [solid-primitives](https://primitives.solidjs.community/).

As the author of a few of those packages, I want to delve into our collection to present you a few gems that might end up being helpful to you. Here's the first one


## `@solid-primitives/context`

If you, like me, make extensive use of context, you probably already know the term "context hell" or can imagine what it looks like:

```tsx
<FirstContext.Provider>
  <SecondContext.Provider>
    <ThirdContext.Provider>
      <AndSoOnContext.Provider>...
```

Wouldn't it be better if you could just compose contexts together like this:

```ts
[FirstContext, SecondContext, ThirdContext, AndSoOnContext, ...]
```


### `MultiProvider` to the rescue!

Long story short, it allows you to compose contexts as an array:

```tsx
import { MultiProvider } from '@solid-primitives/context';

<MultiProvider values={
  [FirstContext, SecondContext, ThirdContext]}>
  ...
</MultiProvider>
```

Even better, you can compose contexts depending on configuration and also add values by using a tuple `[Provider, values]`:

```tsx
const providers = [
  featureOne && FirstContext,
  SecondContext,
  featureThree && ThirdContext,
  [AndSoOnContext.Provider, providerProps]
];
return <MultiProvider values={providers}>...</MultiProvider>;
```

It doesn't matter if you add the context or the context.Provider, MultiProvider will automatically detect what you gave it and handle it correctly. How's that for convenience? This functionality comes at only ~213bytes increase of bundle size¹ and is especially useful for feature toggles and A/B testing.

We're not done yet, though. This package has another export:


### `createContextProvider`

Since we're using a lot of context, we want to reduce the boilerplate that goes into the creation of each of them. Imagine we have a Counter component that encapsulates its state in a context:

```tsx
import { useCounter } from './CounterContext';

function ContextUsingCounter() {
  const { count, increment } = useCounter();
  return <button onClick={increment}>{count()}</button>;
}
```

The context for such a component would probably look like this:

```tsx
import { createContext, createSignal, useContext } from 'solid-js';
import { type JSX } from 'solid-js/web';

const CounterContext = createContext({});

export function CounterProvider(props: { initial?: number, children?: JSX.Elements } = {}) {
  const [count, setCount] = createSignal(props.initial ?? 0);
  const increment = () => setCount(count() + 1);
  return <CounterContext.Provider values={{ count, increment }}>
    {props.children}
  </CounterContext.Provider>;
}

export function useCounter() {
  return useContext(CounterContext)
}
```

Instead, we can leverage `createContextProvider` to wrap things for us, resulting in more concise code:

```tsx
import { createContextProvider } from '@solid-primitives/context';

export const [MyContextProvider, useCounter] =
  createContextProvider((props: { initial: number }) => {
    const [count, setCount] = createSignal(props.initial);
    const increment = () => setCount(count() + 1);
    return { count, increment };
  });
```

Since this primitive adds ~151bytes to your bundle size¹, using it 2-3 times should already decrease your bundle size.


## Final words

I hope you'll find something useful in our collection. However, if you are missing something or have problems using our primitives, don't hesitate to ask for it, either here, or - if you want a fast response - in the `#solid-primitives` channel of the [Solid.js discord](https://discord.com/invite/solidjs).

---

¹: this is the minified/gzipped size of the bundled primitive alone. Depending on your own package, there might be a slight difference in the actual increase of your bundle size, as code that is repeated often gzips better.