Hiding Images with Content Warnings in React
Tags: react, a11y, javascript
Published: 2022-06-15 14:45:10

---
title: Hiding Images with Content Warnings in React
published: true
description: simple, re-usable component for hiding sensitive images in React.
tags: react, accessibility, javascript,
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
---

After attending Tori Clark and Keli Sierra Bradley’s [axe-con talk on trigger and content warnings](https://www.deque.com/axe-con/sessions/my-trigger-my-choice-ux-for-trigger-and-content-warnings/), I put making a toggle-able content warning for my blog on my to-do list. 

By content warning, I’m envisioning something similar to Instagram’s current implementation. In the screenshot included below, the account belonging to horror magazine [Fangoria](https://www.instagram.com/fangoria/) has age-gated some of its especially gnarly images. The image is blurred beyond recognition with text overlaying it that lets users know why it is hidden. Lastly, there is a button to click to reveal the image.

![screen grab of a censored photo on Fangoria's Instagram account](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yctp59apwvuzkf4kd32f.png)

In my past life in academia, I wrote a lot about horror video games, and would like to better integrate that interest into my portfolio. Given that most of my website is about my current experience as a front-end developer, including a warning before the, uh, grosser of that content sounded like a good idea.

In their talk, Clark and Bradley compared content warnings to wet floor signs: the warning benefits everyone, but some people are more likely to slip than others. I interpreted that to mean, if you have an image you think *might* be triggering to someone, it’s best to err on the side of caution and give users the opportunity to opt into seeing it. The worst case is that a user may have to take an extra action to see it, which, I think, is worth the payoff of potentially ruining someone’s day by *not* hiding it. I specifically brought up content warnings in terms of hiding horror, but it could have a lot of other, comparatively benign applications such as hiding a spoiler from a popular TV show or respecting culture-specific sensitivities.

My portfolio currently uses GatsbyJS, so React is going to be the way to go for me, and JSX makes it very easy to hide and show HTML elements inline. That said, this should be relatively easy to adapt to other frameworks like Vue or even vanilla Javascript if we break it down effectively. 

## The elements of our component

Just taking the instagram post as an example, we know we need at least four basic pieces for our component:

1. an image
2. alt text
3. toggle state management
4. warning copy

If we are making this a re-usable React component, we know that at a bare minimum, the image will be different each time. Therefore, we’ll pass the image url and alt text in as props. We may want to start with something like:

```jsx
const ContentWarning = ({imgSrc, alt} => {
	return (<div>
		<img src={imgSrc} alt={alt} />
	</div>)
}
```

Continuing down our list, we know we need a way to manage state, which we can easily use in modern React with the `useState` hook. We’ll also go ahead and throw in our warning text. 

```jsx
const ContentWarning = ({imgSrc, alt} => {
	const [showContent, setShowContent] = React.useState(false);
	return (<div>
		<img src={imgSrc} alt={alt} />
		<div className="warning-text">
              This image may contain sensitive content
     </div>
	</div>)
}
```

So what all do we need to control with our state? We know we want to disguise the image and show our warning text when `showContent` is `false`, but we also should be careful to disguise our alt text so that a screenreader user also isn’t inadvertently exposed to something they don’t want to hear. All images *must* have an `alt` attribute in compliance with [WCAG](https://www.w3.org/TR/WCAG21/), but that attribute can be empty—no space, just empty—so we’ll add a ternary function to check for whether `showContent` is `true`. Similarly we’ll use the logical and operator to only display the content warning if `showContent` is `false`.

Fortunately, blurring the image only requires a single line of code in modern CSS! We will similarly only include that if `showContent` is `false`.

```jsx
const ContentWarning = ({imgSrc, alt} => {
	const [showContent, setShowContent] = React.useState(false);
	return (<div>
		<img 
				src={imgSrc}
				style={{ filter: !showContent ? "blur(1.5rem)" : "none" }}
				alt={showContent ? alt : ""} />
		{!showContent && <div className="warning-text">
              This image may contain sensitive content
     </div>}
	</div>)
}
```

Finally, to manage the state of our content, we need a button for users to click to toggle the image: 

```jsx

const ContentWarning = ({imgSrc, alt} => {
	const [showContent, setShowContent] = React.useState(false);
	return (<div>
		<img 
				src={imgSrc}
				style={{ filter: !showContent ? "blur(1.5rem)" : "none" }}
				alt={showContent ? alt : ""} />
		{!showContent && <div className="warning-text">
              This image may contain sensitive content
     </div>}
<button
          className="toggle-button"
          type="button"
          onClick={() => setShowContent(!showContent)}
        >
          {showContent ? "Hide" : "Show"} Image
        </button>
	</div>)
}

```

([remember to add a `type` to your button, folks!](https://dev.to/heyitsstacey/quick-tip-always-include-a-type-on-your-buttons-3cbh))

##Putting it all together

To use our component, the code would look like this: 

```jsx
<ContentWarning
      imgSrc="https://picsum.photos/id/1025/500/300"
      alt="an adorable pug sitting upright while wrapped in a blanket in the woods"
    />
```

So we put that all together and we see our image hiding and showing based on our state… all good! ….Right? 

Actually, there is at least one more step. Remember how I mentioned we also wanted to render the alt text conditionally based on whether the content was hidden or not? In its current implementation, this *does* add the alt text back to the page when we show the image, but a screen reader user won't immediately get that new information like a sighted user will when they see the instant visual change. There are a couple ways to solve for this, and I think the simplest solution may be to just wrap our image in a [live region](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions). 

```jsx
const ContentWarning = ({ imgSrc, alt }) => {
    const [showContent, setShowContent] = React.useState(false);

    return (
      <div>
        <div aria-live="polite">
          <img
            style={{ filter: !showContent ? "blur(1.5rem)" : "none" }}
            src={imgSrc}
            alt={showContent ? alt : ""}
          />
          {!showContent && (
            <div className="warning-text">
              This image may contain sensitive content
            </div>
          )}
        </div>
        <button
          className="toggle-button"
          type="button"
          onClick={() => setShowContent(!showContent)}
        >
          {showContent ? "Hide" : "Show"} Image
        </button>
      </div>
    );
  }; 
```

This way, it will announce the new text after a user hits the toggle button.

Here’s a [Codepen example](https://codepen.io/heyitsstacey/pen/BaYvver) with some small style tweaks to make it a little more presentable (don’t worry, the image is an inoffensive and *very* cute dog).

## Potential enhancements

### Global Toggles

On social media platforms like Twitter, users have the option of opting in and out of seeing “media that may contain sensitive content.” We therefore may also want to have a site-wide toggle that will hide or reveal all images on the page. 

To do this: instead of just passing the image url and alt text as props, we would also pass something like `globalShowContent`

```jsx
const ContentWarning = ({ imgSrc, alt, globalShowContent}) => {}
```

If we still want users to be able to toggle individual images, we would start by setting our component level state as `const [showContent, setShowContent] = React.useState(globalShowContent);` to respect the global setting from the jump.

### Srcset instead of Src

Since we are well into 2022 at this point, you may want to pass in multiple image sources a la [srcset](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-srcset), to do this, in addition to passing in a string with `imgSrc`, we could pass an array of strings containing our urls and sizes (`["https://picsum.photos/id/1025/500/300 x1”, “https://picsum.photos/id/1025/1000/600 x2”]`), and map it in our component.

```jsx
<img src={imgSrc}
     alt={showContent ? alt : ""}
		 srcSet={imgSrcSet.map(imgSrc=>imgSrc)} />
```
## Conclusion

As Clark and Bradley called out in their talk, ensuring users can safely access your content is an accessibility issue, and content warnings are a relatively straightforward way to do that. Whether it’s gating potentially-triggering content or just hiding a spoiler, it’s worth putting basic safe guards in place to ensure your users have a smooth, pain-free experience. Hopefully this example proves to be an easy baseline for starting your component.

_Edit to correct typos; cross-posted from my site_