Is this a React Anti-pattern?

I am working on a permissions component that uses nested children, nonreactive state, and global space. It feels like an anti-pattern. But it is so simple and clean and seems to work.

Is this a React Anti-pattern?
Photo by Gemma Evans / Unsplash

I only recently started to use React again. Truth be told, while it has gotten a lot better. Vue and Svelte feel a lot more modern and easier to use at times. Vue specifically has the name structures in a great place. And Svelte is just so terse and easy to write.

Recently I wanted to create a component that governs access to a feature. It needs to render the content if it is allowed, or render alternate content if disallowed.

A realistic example may be rendering an “Edit” button in a notes app if you have permission to edit notes. If you do not, instead of rendering a disabled button so the user understands what permissions they don’t have.

The Feature the component is pretty straightforward. It checks against the store to see if the feature request is allowed. In this case, it is using a zustand store. I am accessing it via getState() because I don’t want the component to be reactive, requiring a new login if the permissions change.

import { useAccessStore } from "../lib/stores";

let access = false;

// Only rendered when access is allowed
const Allow = function FeatureAllow({ children }) {
  return access ? children : null;

// Only rendered when access is disallowed
const Disallow = function FeatureDisallow({ children }) {
  return access ? null : children;

// Evaluates a string of feature name
// Feature name must be present and true to pass
export default function Feature({ children, checks }) {
  // Sets access based on if the feature is present and true within the useAccessStore
  access = useAccessStore().getState().features?.[checks] ?? false;

  return children;

Feature.Allow = Allow;
Feature.Disallow = Disallow;

The design is essentially three tags -

  • <Feature> - Handles the check against the access store
  • <Feature.Allow> - Handles what to render when access is granted
  • <Feature.Disallow> - Handles what to render when access is not granted

Here is a example of usage.

<Feature checks="edit_note">
    <button className="p-4 text-center text-blue-500 hover:text-blue-400">Edit</button>
    <button disabled className="disabled p-4 text-center text-gray-500 cursor-pointer">Edit</button>

I love it, it works, it is simple, it is clear and easy to maintain. But it feels like an anti-pattern.

  • It’s not reactive. Right now I am using this with SSR, and kind of want to keep it that way. But it feels like it should be reactive for future proof.
  • The “state” of allow, just hangs of global space in the Feature.jsx component file. This seems to work without conflicting with other instances. But I don’t know if this breaks hydration or other more complicated items later on.
  • Attaching the .Allow and .Disallow off of the parent feels like magic. But is it best practice? Not sure how this impacts bundling and tree shaking.

Hit me up on Twitter. I am curious if this is the best way to handle this. Here is the thread.