diff --git a/content/docs/components-and-props.md b/content/docs/components-and-props.md
index b6e967c7..505ef3c1 100644
--- a/content/docs/components-and-props.md
+++ b/content/docs/components-and-props.md
@@ -76,7 +76,7 @@ ReactDOM.render(
);
```
-Try it on CodePen.
+[](codepen://components-and-props/rendering-a-component).
Let's recap what happens in this example:
@@ -118,7 +118,7 @@ ReactDOM.render(
);
```
-Try it on CodePen.
+[](codepen://components-and-props/composing-components).
Typically, new React apps have a single `App` component at the very top. However, if you integrate React into an existing app, you might start bottom-up with a small component like `Button` and gradually work your way to the top of the view hierarchy.
@@ -152,7 +152,7 @@ function Comment(props) {
}
```
-Try it on CodePen.
+[](codepen://components-and-props/extracting-components).
It accepts `author` (an object), `text` (a string), and `date` (a date) as props, and describes a comment on a social media website.
@@ -231,7 +231,7 @@ function Comment(props) {
}
```
-Try it on CodePen.
+[](codepen://components-and-props/extracting-components-continued).
Extracting components might seem like grunt work at first, but having a palette of reusable components pays off in larger apps. A good rule of thumb is that if a part of your UI is used several times (`Button`, `Panel`, `Avatar`), or is complex enough on its own (`App`, `FeedStory`, `Comment`), it is a good candidate to be a reusable component.
diff --git a/content/docs/hello-world.md b/content/docs/hello-world.md
index 29c0f839..adf4db48 100644
--- a/content/docs/hello-world.md
+++ b/content/docs/hello-world.md
@@ -12,7 +12,7 @@ redirect_from:
- "docs/getting-started-zh-CN.html"
---
-The easiest way to get started with React is to use this Hello World example code on CodePen. You don't need to install anything; you can just open it in another tab and follow along as we go through examples. If you'd rather use a local development environment, check out the [Installation](/docs/installation.html) page.
+The easiest way to get started with React is to use [this Hello World example code on CodePen](codepen://hello-world). You don't need to install anything; you can just open it in another tab and follow along as we go through examples. If you'd rather use a local development environment, check out the [Installation](/docs/installation.html) page.
The smallest React example looks like this:
diff --git a/content/docs/introducing-jsx.md b/content/docs/introducing-jsx.md
index 11dabf04..d636e0c5 100644
--- a/content/docs/introducing-jsx.md
+++ b/content/docs/introducing-jsx.md
@@ -46,7 +46,7 @@ ReactDOM.render(
);
```
-Try it on CodePen.
+[](codepen://introducing-jsx).
We split JSX over multiple lines for readability. While it isn't required, when doing this, we also recommend wrapping it in parentheses to avoid the pitfalls of [automatic semicolon insertion](http://stackoverflow.com/q/2846283).
diff --git a/gatsby-config.js b/gatsby-config.js
index 01961f1e..60be836c 100644
--- a/gatsby-config.js
+++ b/gatsby-config.js
@@ -56,6 +56,7 @@ module.exports = {
},
},
'gatsby-remark-autolink-headers',
+ 'gatsby-remark-codepen-examples',
'gatsby-remark-use-jsx',
{
resolve: 'gatsby-remark-prismjs',
diff --git a/package.json b/package.json
index 82a862d6..8501d1de 100644
--- a/package.json
+++ b/package.json
@@ -85,6 +85,7 @@
},
"devDependencies": {
"eslint-config-prettier": "^2.6.0",
- "recursive-readdir": "^2.2.1"
+ "recursive-readdir": "^2.2.1",
+ "unist-util-map": "^1.0.3"
}
}
diff --git a/plugins/gatsby-remark-codepen-examples/index.js b/plugins/gatsby-remark-codepen-examples/index.js
new file mode 100644
index 00000000..d3872849
--- /dev/null
+++ b/plugins/gatsby-remark-codepen-examples/index.js
@@ -0,0 +1,57 @@
+const {existsSync} = require('fs');
+const {join} = require('path');
+const map = require('unist-util-map');
+
+const CODEPEN_PROTOCOL = 'codepen://';
+const DEFAULT_LINK_TEXT = 'Try it on CodePen';
+
+// TODO target="_blank"
+module.exports = ({markdownAST}) => {
+ map(markdownAST, (node, index, parent) => {
+ // eg convert
+ // from: [](codepen:introducing-jsx)
+ // to: Try it on CodePen
+ // from: [Try the Hello World example on CodePen](codepen:hello-world)
+ // to: Try the Hello World example on CodePen
+ if (node.type === 'link' && node.url.startsWith(CODEPEN_PROTOCOL)) {
+ const href = node.url.replace(CODEPEN_PROTOCOL, '/codepen/');
+ const text =
+ node.children.length === 0 ? DEFAULT_LINK_TEXT : node.children[0].value;
+
+ // Verify that the specified example file exists.
+ const filePath = join(__dirname, `../../${href}.js`);
+ if (!existsSync(filePath)) {
+ console.error(
+ `Invalid Codepen link specified; no such file "${filePath}"`,
+ );
+ process.exit(1);
+ }
+
+ const anchorOpenNode = {
+ type: 'html',
+ value: ``,
+ };
+
+ const textNode = {
+ type: 'text',
+ value: text,
+ };
+
+ const anchorCloseNode = {
+ type: 'html',
+ value: '',
+ };
+
+ parent.children.splice(
+ index,
+ 1,
+ anchorOpenNode,
+ textNode,
+ anchorCloseNode,
+ );
+ }
+
+ // No change
+ return node;
+ });
+};
diff --git a/plugins/gatsby-remark-codepen-examples/package.json b/plugins/gatsby-remark-codepen-examples/package.json
new file mode 100644
index 00000000..f6017a58
--- /dev/null
+++ b/plugins/gatsby-remark-codepen-examples/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "gatsby-remark-codepen-examples",
+ "version": "0.0.1"
+}
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index c527a80a..9640d346 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9795,6 +9795,12 @@ unist-util-is@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-2.1.1.tgz#0c312629e3f960c66e931e812d3d80e77010947b"
+unist-util-map@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/unist-util-map/-/unist-util-map-1.0.3.tgz#26a913d7cddb3cd3e9a886d135d37a3d1f54e514"
+ dependencies:
+ object-assign "^4.0.1"
+
unist-util-modify-children@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/unist-util-modify-children/-/unist-util-modify-children-1.1.1.tgz#66d7e6a449e6f67220b976ab3cb8b5ebac39e51d"