Living on the edge with “dangerous”

featured
January 13, 2019
💫 Originally posted here. Broken? Let me know ~

As I’ve been using Styled Components (SC hereafter) and been wondering about the magic behind it.

Thankfully, Max Stoiber (a creator of SC) has written an article, The magic behind styled-components on how Styled Components work with Tagged Template Literals.

At the end of article, Max finished the article with following sentence.

I’m very excited to see what other use cases for tagged template literals people come up with!

Max Stoiber

So I created dangerous, a tagged template literal function, which accepts a dangerous text (such as malicious user input) and sets inner HTML of a component in SC like style.


Prerequisite

I will skip on what tagged template literal is or how it works as Max has explained quite well.


☢ What is “dangerous”?

When you pass a string to component, they are encoded to prevent malicious user input/scripts.

React team has made it hard to set innerHTML of a component to help developers accidentally set malicious string to prevent XSS attacks.

But dangerous makes it easy to set an innerHTML value and returns a component with the unsafe HTML string.

const Dangerous = dangerous.div`
<h1>Who am I?</h1>
<p>Last Name is "${props => props.lastName}"</p>
<p>First Name is "${props => props.firstName}"</p>
<a href="javascript:alert('hi');">Show Alert</a>
`;
function App() {
return <Dangerous firstName="Sung" lastName="Kim" />;
}
view raw DangerousApp.js hosted with ❤ by GitHub
Result of DangerousApp.js

As you can with SC, you can pass a component or an HTML tag to the dangerous like or even wrap SC component and vice versa.

import styled from "styled-components";
import dangerous from "dangerous";
import "./styles.css";
// const Block = ({ ...props }) => <div {...props} />;
class Block extends React.Component {
// To check if static fields are hoised correctly
static count = 10;
static increaseCount = () => console.log(++Block.count);
static decreaseCount = () => console.log(--Block.count);
render() {
return <div {...this.props} />;
}
}
// const Dangerous = dangerous.div`
const Dangerous = dangerous(Block)`
<h1>Who am I?</h1>
<p>Last Name is "${props => props.lastName}"</p>
<p>First Name is "${props => props.firstName}"</p>
<a href="javascript:alert('hi');">Show Alert</a>
`;
const StyledDangerous = styled(Dangerous)`
background-color: papayawhip;
padding: 2em 0;
`;
const StyledBlock = styled(Block)`
background-color: hotpink;
padding: 2em 0;
`;
const DangerousStyled = dangerous(StyledBlock)`
<h1>Alter Ego</h1>
<p>Click link below to find out who my alter ego is</p>
<a href="javascript:alert('${props => props.name}');">Show Aleter Ego Name</a>
`;
function App() {
return (
<div className="App">
<section>
<StyledDangerous firstName="Sung" lastName="Kim" />
<DangerousStyled name="dance2die" />
</section>
</div>
);
}

So if you need to style the dangerous component, either pass a SC component or wrap the dangerous component in SC component.


🤔 Why did you create it then?

I will be frank. It’s quite useless in practice and promotes bad behavior.

It was mostly an academic project to learn how tagged template literal works & get used to TypeScript.

And a plan to migrate to TypeScript for React, and learn how to test it.


💀 Failures?

I’ve implemented it on CodeSandbox using vanilla JavaScript but wanted to try TypeScript.

Like a kid with FOMO

  • Creating a JavaScript library with Webpack when using TypeScript wasn’t as easy as I had anticipated.
    1. Trying to do/learn too many things at once (TypeScript, tagged template literal, CircleCI, etc) really made it worse.
    2. I’ve given up on writing tests in the prev v1.0 release (currently it’s at 0.1.x).
    3. I decided to hold off after finishing Kent C. Dodd’s Testing JavaScript course.

So the easy way out was to use RollUp, which I’ve used before.

But with TypeScript in play, it was harder as TypeScript has its own transpiler.

I also ended up bloating up the library size and had to learn how to fix it.

Fixed by marking react methods as external dependencies in tsconfig.

Size comparison on BundlePhobia

I remember making the same mistake while first learning React, trying to learn Redux, Node, Express, and all other related technologies all at once and got no where after shaving the yak for months.

But learning from these failures (try to use Rollup for simple libraries, don’t use new language [typescript] without understanding how it works first, etc) was a great lesson.


👋 Parting Words

Many thanks to Max Stoiber to his blog & Styled Components for sharing his knowledge and code in public.

Frankly speaking, I’ve never had much need to use dangerouslySetInnerHTML at all.

Please don’t hesitate to give me any feedback (good or harsh, both are welcome) or changes required.

🏔 Resources