Conceal Spoilers with a Custom React Component
Create a custom React component that conceals spoilers, but allows users the ability to click and reveal the contents.
People love to discuss their favorite TV shows, movies, and books on the Internet. My favorite place to do this is on Reddit, but if I'm not careful and don't alert other users to possible spoilers in a post or comment that I've written, then I will be downvoted into oblivion.
Luckily for us, Reddit has a neat markdown feature that allows people to conceal text from other Redditors, but it reveals the secret text once it has been clicked. It's a very simple solution, and I will show you how it's done in React.
Let's start with a very minimal React application. I've created the main App
component and have imported our custom SpoilerText
. As you can see, it wraps around other JSX elements.
Implementing it is very easy. The JSX elements that are wrapped around SpoilerText
are accessed with props.children
. We wrap the child elements in a <div>
that has a CSS class applied to it: either .App-hidden
or .App-notHidden
.
We keep track of whether it's hidden or not with the useState
hook. Initially this hidden
value is true when the component is rendered for the first time, but as soon as the user clicks on the element, the reveal
function is invoked and it updates the value to false.
Now all you need is a bit of CSS to complete the effect.
To recursively select all child elements that are inside of a class called .App-hidden
, then use the > *
css selector. We set the background-color and text color to black, so that it appears to have been censored and redacted. For a better UI experience, I have added a cursor: pointer
rule so that a user knows the element is clickable when they hover their mouse pointer over the hidden text.
I have also added the ::selection
pseudo class, so that a spoiler is not accidentally revealed if the user selects or highlights the text.
.App-notHidden
simply unsets all of the CSS properties that were applied in .App-hidden
, and this is all achieved with a simple animation. By using transition: all 1s
, the text and background color slowly fade back to defaults. You can control the speed of this and use decimal values or even milliseconds.
That's it! Pretty easy, right?
This component can be extended and support more features very easily. For example, the default color is black and the default animation speed is 1 second. But you could very easily accept optional props to customize the look and feel of this component