Managing z-indexes within CSS-in-JS
ReactJS CSS JavaScriptManaging z-indexes can be tricky. You'll undoubtedly be familiar with examples like these:
.header {
    position: sticky;
    z-index: 100; /* Keep sticky header above other content */
}
.popup {
    z-index: 99999; /* Must be the absolute top */
}
While it works, it's not particularly elegant. Searching through your codebase for all z-index values could easily yield something like [-1, 1, 2, 3, 4, 5, 10, 11, 19, 100, 200, 299, ..., 99999]. Have fun choosing the next number in that sequence (or worse, adding something between 2 and 3).
If your CSS is generated using a CSS-in-JS library (e.g. styled-components, Linaria, JSS...) a fairly clean way to organize your z-indexes is by defining them in an array:
const zIndexOrder = [
  'body',
  'header',
  'popup',
];
const zIndexes = zIndexOrder.reduce(
  (acc, current, index) => {
    acc[current] = index;
    return acc;
  },
  {}
);
export default zIndexes;
Then use it in your component as follows:
import styled from 'styled-components';
import zIndexes from '../style/zIndexes';
const MyHeader = styled.header`
  z-index: ${zIndexes.header};
`;
The neat thing about this is adding an additional z-index becomes trivial:
const zIndexOrder = [
  'body',
  'aside', // New
  'header',
  'popup',
];
The z-index number values automatically update.
TypeScript variant #
In TypeScript you could do the following:
const zIndexOrder = [
  'body',
  'header',
  'popup',
] as const;
type ZIndexValues = typeof zIndexOrder[number];
type ZIndexRecord = Record<ZIndexValues, number>;
const zIndexes = zIndexOrder.reduce(
  (acc: ZIndexRecord, current: ZIndexValues, index: number) => {
    acc[current] = index;
    return acc;
  },
  {} as ZIndexRecord
);
export default zIndexes;
It's a bit convoluted but does guarantee type-safety in your component:
import styled from 'styled-components';
import zIndexes from '../style/zIndexes';
// The ollowing gives an error:
// TS2551: Property 'headr' does not exist on type 'ZIndexRecord'. Did you mean 'header'?
const MyHeader = styled.header`
  z-index: ${zIndexes.headr};
`;
Beware your stacking contexts #
A z-index is relative to other elements within what's called a "stacking context". The main root element (<html />) creates such a stacking context. However, there are various ways you can (usually unknowingly) create a new stacking context. MDN lists all possible causes.
As a consequence, an element with a high z-index can be rendered beneath an element with a low z-index. This can be counter-intuitive and confusing at first.
The most common cause for this problem I've seen is when using position on some parent element while trying to set a z-index on a child element.
For example:
<div class="red-parent">
  <div class="red-child">
    Red
  </div>
</div>
<div class="blue-parent">
  <div class="blue-child">
    Blue
  </div>
</div>
.red-parent {
  position: absolute; /* this causes a new stacking context */
  border: 2px dashed DarkRed;
}
.red-child {
  padding: 2rem;
  background-color: IndianRed;
  z-index: 999; /* despite this high value, red still appears below blue */
}
.blue-parent {
  position: absolute; /* this causes a new stacking context */
  top: 4rem;
  left: 3rem;
  border: 2px dashed DarkBlue;
}
.blue-child {
  padding: 2rem;
  background-color: CornflowerBlue;
}

Despite .red-child { z-index: 999 } the blue block appears on top. This is caused by .red-parent { position: absolute } which creates a new stacking context for all of .red-parent's children.
A new stacking context only affects the element's children; the z-index of the positioned element itself is still part of the original stacking context.
That is why blue appears on top of red; both .blue-parent and .red-parent have the default z-index: 0. Because they have the same z-index value within the same root stacking context the last element in HTML is drawn on top (see Stacking without the z-index property).
Usually I fix this issue by moving the z-index to the positioned element. I.e:
.red-parent {
  position: absolute;
  border: 2px dashed DarkRed;
  z-index: 999; /* move z-index to the positioned element */
}
.red-child {
  padding: 2rem;
  background-color: IndianRed;
  /* remove z-index from child */
}

Why does this matter for the zIndexes-array approach? #
Having only one zIndexes-array would logically correspond to one stacking context: the root stacking context.
But; since it's often unclear when a new stacking context is created you could easily end up having one zIndexes-array that (tries) to manage multiple stacking contexts. This could lead to unexpected results.
In most layouts I find z-indexes can be limited to the main positioned element and one zIndexes-array is all I need for the whole app.
If I really do need a separate stacking context, I'll define a second zIndexes-array for that secondary stacking context (e.g. zMenuIndexes in Menu.js).
My reasoning for this is it is pretty rare to require z-indexes in a non-root stacking context; so it is fine -and arguably preferable- to make that explicit rather than trying to manage all zIndexes in a single array.
SASS #
You can do something similar with SASS as well: Handling z-index with SASS.