React/NextJS Timer-based fade in/fade out of text not working
You are in the right path. But you need to fix a few things. Instead of doing:
 <span className={`styles.${fadeProp}`}>
      {WORDS_TO_ANIMATE[wordOrder]}
 </span>

Try it with
 <span className={fadeProp}>
      {WORDS_TO_ANIMATE[wordOrder]}
 </span>

The reason being is that you were not setting your css class name selector correclty. I think after that you can tweak it for you convenience.
------ EDIT -----
Also, you will need to create a .css file, not .module.css. I think the reason  is because the .module.css are compiled and cannot be dynamic.
Create a global.css file inside styles dir. Import it in you _app.js entrypoint inside pages dir.
If you do not have it, it looks something like this:
_app.js
import '../styles/globals.css';
 
// This default export is required in a new `pages/_app.js` file.
export default function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />;
}


Using globals.css is not the best option but you can imrove that later on. Please find more info about styling Nextjs apps here.

I'm relatively new to React and NextJS. I've spent a good 8 hours and a fair amount of research trying to figure this out with no luck!
I'm trying to set up a component that picks a word at random from an array, fades this in, then after a delay fades it out and fades in a new word.
After hacking together several of my own failed solutions, I found a tutorial and tried their code (with some changes to shift it from TS to JS) but it still doesn't work.
I can get the text to change on a timer easily enough, but not the fading.
Any ideas?
Using NextJS 13, built on StackBlitz's basic NextJS setup
index.js
import { useEffect, useState } from 'react';
import styles from './index.module.css';

const FADE_INTERVAL_MS = 2000;
const WORD_CHANGE_INTERVAL_MS = FADE_INTERVAL_MS * 2;
const WORDS_TO_ANIMATE = [
  'Hello',
  'Ciao',
  'Jambo',
  'Bonjour',
  'Salut',
  'Hola',
  'Nǐ hǎo',
  'Hallo',
  'Hej',
  '👋🏻',
];

export default function AnimatedText() {
  const [fadeProp, setFadeProp] = useState('fadeout');
  const [wordOrder, setWordOrder] = useState(0);

  useEffect(() => {
    const fadeTimeout = setInterval(() => {
      fadeProp === 'fadein' ? setFadeProp('fadeout') : setFadeProp('fadein');
    }, FADE_INTERVAL_MS);

    return () => clearInterval(fadeTimeout);
  }, [fadeProp]);

  useEffect(() => {
    const wordTimeout = setInterval(() => {
      setWordOrder(
        (prevWordOrder) => (prevWordOrder + 1) % WORDS_TO_ANIMATE.length
      );
    }, WORD_CHANGE_INTERVAL_MS);

    return () => clearInterval(wordTimeout);
  }, []);

  return (
    <h2 className={styles.white}>
      <span className={`styles.${fadeProp}`}>
        {WORDS_TO_ANIMATE[wordOrder]}
      </span>
      , I'm bleh.
    </h2>
  );
}


index.module.css
.fadein {
  animation: fadein 0.3s;
  -moz-animation: fadein 0.3s;
  -webkit-animation: fadein 0.3s;
  -o-animation: fadein 0.3s;
}

.fadein {
  animation: fadeout 0.3s;
  -moz-animation: fadeout 0.3s;
  -webkit-animation: fadeout 0.3s;
  -o-animation: fadeout 0.3s;
}

.white {
  color: silver;
}

@keyframes fadein {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

@keyframes fadeout {
  0% {
    opacity: 1;
    color: blue;
  }
  100% {
    opacity: 0;
    color: red;
  }
}


