Repository: styleguidist/react-styleguidist
Pull request #1471 (2019-11-15 19:18:36)

Refactor: move on the typescript transpilation of the components
- Markdown
- Editor
- Argument
- Errors

#1352

codecov-io: # [Codecov](https://codecov.io/gh/styleguidist/react-styleguidist/pull/1471?src=pr&el=h1) Report
> Merging [#1471](https://codecov.io/gh/styleguidist/react-styleguidist/pull/1471?src=pr&el=desc) into [master](https://codecov.io/gh/styleguidist/react-styleguidist/commit/7564df92a68a3121ffeb4bbd4a4803087fcde939?src=pr&el=desc) will **increase** coverage by `<.01%`.
> The diff coverage is `100%`.

| [Impacted Files](https://codecov.io/gh/styleguidist/react-styleguidist/pull/1471?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [...ent/rsg-components/Arguments/ArgumentsRenderer.tsx](https://codecov.io/gh/styleguidist/react-styleguidist/pull/1471/diff?src=pr&el=tree#diff-c3JjL2NsaWVudC9yc2ctY29tcG9uZW50cy9Bcmd1bWVudHMvQXJndW1lbnRzUmVuZGVyZXIudHN4) | `100% <100%> (ø)` | |
| [src/client/rsg-components/Code/CodeRenderer.tsx](https://codecov.io/gh/styleguidist/react-styleguidist/pull/1471/diff?src=pr&el=tree#diff-c3JjL2NsaWVudC9yc2ctY29tcG9uZW50cy9Db2RlL0NvZGVSZW5kZXJlci50c3g=) | `100% <100%> (ø)` | |
| [src/client/rsg-components/Error/ErrorRenderer.tsx](https://codecov.io/gh/styleguidist/react-styleguidist/pull/1471/diff?src=pr&el=tree#diff-c3JjL2NsaWVudC9yc2ctY29tcG9uZW50cy9FcnJvci9FcnJvclJlbmRlcmVyLnRzeA==) | `100% <100%> (ø)` | |
| [src/client/rsg-components/Editor/Editor.tsx](https://codecov.io/gh/styleguidist/react-styleguidist/pull/1471/diff?src=pr&el=tree#diff-c3JjL2NsaWVudC9yc2ctY29tcG9uZW50cy9FZGl0b3IvRWRpdG9yLnRzeA==) | `100% <100%> (ø)` | |
| [...onents/PlaygroundError/PlaygroundErrorRenderer.tsx](https://codecov.io/gh/styleguidist/react-styleguidist/pull/1471/diff?src=pr&el=tree#diff-c3JjL2NsaWVudC9yc2ctY29tcG9uZW50cy9QbGF5Z3JvdW5kRXJyb3IvUGxheWdyb3VuZEVycm9yUmVuZGVyZXIudHN4) | `100% <100%> (ø)` | |
| [...lient/rsg-components/Argument/ArgumentRenderer.tsx](https://codecov.io/gh/styleguidist/react-styleguidist/pull/1471/diff?src=pr&el=tree#diff-c3JjL2NsaWVudC9yc2ctY29tcG9uZW50cy9Bcmd1bWVudC9Bcmd1bWVudFJlbmRlcmVyLnRzeA==) | `100% <100%> (ø)` | |

sapegin: Do you want to do anything or I can merge it? 🕺 
elevatebart: Go right ahead and merge it ;-)

Bart Ledoux
On Nov 19, 2019, 03:28 -0600, Artem Sapegin <notifications@github.com>, wrote:
> Do you want to do anything or I can merge it? 🕺
> —
> You are receiving this because you authored the thread.
> Reply to this email directly, view it on GitHub, or unsubscribe.


Patch:
diff --git a/package-lock.json b/package-lock.json
index ca8e46dc..27dedec4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1504,6 +1504,15 @@
       "integrity": "sha512-ogI4g9W5qIQQUhXAclq6zhqgqNUr7UlFaqDHbch7WLSLeeM/7d3CRaw7GLajxvyFvhJqw4Rpcz5bhoaYtIx6Tg==",
       "dev": true
     },
+    "@types/markdown-to-jsx": {
+      "version": "6.9.0",
+      "resolved": "https://registry.npmjs.org/@types/markdown-to-jsx/-/markdown-to-jsx-6.9.0.tgz",
+      "integrity": "sha512-LO/oxz+ZfwBDciiVGqLhhdyeWt196kgICe0QS88K1a2u/FgUF1QkeMAm4zdnAo1kNgo2KgFP1Uqy2IiPJLWppA==",
+      "dev": true,
+      "requires": {
+        "@types/react": "*"
+      }
+    },
     "@types/minimatch": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@@ -1520,6 +1529,12 @@
       "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
       "dev": true
     },
+    "@types/prismjs": {
+      "version": "1.16.0",
+      "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.16.0.tgz",
+      "integrity": "sha512-mEyuziLrfDCQ4juQP1k706BUU/c8OGn/ZFl69AXXY6dStHClKX4P+N8+rhqpul1vRDA2VOygzMRSJJZHyDEOfw==",
+      "dev": true
+    },
     "@types/prop-types": {
       "version": "15.7.3",
       "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
@@ -6292,9 +6307,9 @@
       "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ=="
     },
     "handlebars": {
-      "version": "4.4.5",
-      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.4.5.tgz",
-      "integrity": "sha512-0Ce31oWVB7YidkaTq33ZxEbN+UDxMMgThvCe8ptgQViymL5DPis9uLdTA13MiRPhgvqyxIegugrP97iK3JeBHg==",
+      "version": "4.5.2",
+      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.2.tgz",
+      "integrity": "sha512-29Zxv/cynYB7mkT1rVWQnV7mGX6v7H/miQ6dbEpYTKq5eJBN7PsRB+ViYJlcT6JINTSu4dVB9kOqEun78h6Exg==",
       "dev": true,
       "requires": {
         "neo-async": "^2.6.0",
@@ -12793,9 +12808,9 @@
       "dev": true
     },
     "uglify-js": {
-      "version": "3.6.4",
-      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.4.tgz",
-      "integrity": "sha512-9Yc2i881pF4BPGhjteCXQNaXx1DCwm3dtOyBaG2hitHjLWOczw/ki8vD1bqyT3u6K0Ms/FpCShkmfg+FtlOfYA==",
+      "version": "3.6.9",
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.9.tgz",
+      "integrity": "sha512-pcnnhaoG6RtrvHJ1dFncAe8Od6Nuy30oaJ82ts6//sGSXOP5UjBMEthiProjXmMNHOfd93sqlkztifFMcb+4yw==",
       "dev": true,
       "optional": true,
       "requires": {
diff --git a/package.json b/package.json
index 1cfd9328..a70a3211 100644
--- a/package.json
+++ b/package.json
@@ -108,7 +108,9 @@
     "@types/jest": "^24.0.21",
     "@types/keymirror": "^0.1.1",
     "@types/lodash": "^4.14.144",
+    "@types/markdown-to-jsx": "^6.9.0",
     "@types/node": "^12.12.3",
+    "@types/prismjs": "^1.16.0",
     "@types/react": "^16.9.11",
     "@types/react-dom": "^16.9.3",
     "@types/webpack-env": "^1.14.1",
diff --git a/src/client/rsg-components/Argument/Argument.spec.js b/src/client/rsg-components/Argument/Argument.spec.tsx
similarity index 95%
rename from src/client/rsg-components/Argument/Argument.spec.js
rename to src/client/rsg-components/Argument/Argument.spec.tsx
index 3d79cd8a..b97710bc 100644
--- a/src/client/rsg-components/Argument/Argument.spec.js
+++ b/src/client/rsg-components/Argument/Argument.spec.tsx
@@ -1,11 +1,14 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import { ArgumentRenderer, styles } from './ArgumentRenderer';
+import classes from '../../../../test/classes';
 
 const name = 'Foo';
 const type = { name: 'Array' };
 const description = 'Converts foo to bar';
 const props = {
 	classes: classes(styles),
+	name: 'argname',
 };
 
 it('should render argument', () => {
diff --git a/src/client/rsg-components/Argument/ArgumentRenderer.js b/src/client/rsg-components/Argument/ArgumentRenderer.tsx
similarity index 61%
rename from src/client/rsg-components/Argument/ArgumentRenderer.js
rename to src/client/rsg-components/Argument/ArgumentRenderer.tsx
index ef494325..f892d454 100644
--- a/src/client/rsg-components/Argument/ArgumentRenderer.js
+++ b/src/client/rsg-components/Argument/ArgumentRenderer.tsx
@@ -1,18 +1,37 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
+import Styled, { Theme, JssInjectedProps } from 'rsg-components/Styled';
 import Markdown from 'rsg-components/Markdown';
 import Name from 'rsg-components/Name';
 import Type from 'rsg-components/Type';
 import Group from 'react-group';
 
-export const styles = ({ space }) => ({
+export const styles = ({ space }: Theme) => ({
 	block: {
 		marginBottom: space[2],
 	},
 });
 
-export function ArgumentRenderer({ classes, name, type, description, returns, block, ...props }) {
+export interface ArgumentProps {
+	name: string;
+	type?: any;
+	default?: string;
+	description?: string;
+	returns?: boolean;
+	block?: boolean;
+}
+
+type ArgumentPropsWithClasses = ArgumentProps & JssInjectedProps;
+
+export const ArgumentRenderer: React.FunctionComponent<ArgumentPropsWithClasses> = ({
+	classes,
+	name,
+	type,
+	description,
+	returns,
+	block,
+	...props
+}) => {
 	const isOptional = type && type.type === 'OptionalType';
 	const defaultValue = props.default;
 	if (isOptional) {
@@ -44,11 +63,11 @@ export function ArgumentRenderer({ classes, name, type, description, returns, bl
 	}
 
 	return content;
-}
+};
 
 ArgumentRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
-	name: PropTypes.string,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
+	name: PropTypes.string.isRequired,
 	type: PropTypes.object,
 	default: PropTypes.string,
 	description: PropTypes.string,
@@ -56,4 +75,4 @@ ArgumentRenderer.propTypes = {
 	block: PropTypes.bool,
 };
 
-export default Styled(styles)(ArgumentRenderer);
+export default Styled<ArgumentPropsWithClasses>(styles)(ArgumentRenderer);
diff --git a/src/client/rsg-components/Argument/__snapshots__/Argument.spec.js.snap b/src/client/rsg-components/Argument/__snapshots__/Argument.spec.tsx.snap
similarity index 81%
rename from src/client/rsg-components/Argument/__snapshots__/Argument.spec.js.snap
rename to src/client/rsg-components/Argument/__snapshots__/Argument.spec.tsx.snap
index b7e4fb86..9f79a4ee 100644
--- a/src/client/rsg-components/Argument/__snapshots__/Argument.spec.js.snap
+++ b/src/client/rsg-components/Argument/__snapshots__/Argument.spec.tsx.snap
@@ -57,6 +57,12 @@ exports[`should render default value of argument 1`] = `
 <Group
   separator=" "
 >
+  <span>
+    <Styled(Name)>
+      argname
+    </Styled(Name)>
+    :
+  </span>
   <Styled(Type)>
     String
     =bar
@@ -73,6 +79,12 @@ exports[`should render default value of optional argument 1`] = `
 <Group
   separator=" "
 >
+  <span>
+    <Styled(Name)>
+      argname
+    </Styled(Name)>
+    :
+  </span>
   <Styled(Type)>
     Boolean
     ?
@@ -90,6 +102,12 @@ exports[`should render optional argument 1`] = `
 <Group
   separator=" "
 >
+  <span>
+    <Styled(Name)>
+      argname
+    </Styled(Name)>
+    :
+  </span>
   <Styled(Type)>
     Array
     ?
@@ -107,6 +125,12 @@ exports[`should render return value 1`] = `
   separator=" "
 >
   Returns
+  <span>
+    <Styled(Name)>
+      argname
+    </Styled(Name)>
+    :
+  </span>
   <Styled(Type)>
     Array
   </Styled(Type)>
@@ -124,6 +148,12 @@ exports[`should render with block styles 1`] = `
 >
   <Group
     separator=" "
-  />
+  >
+    <span>
+      <Styled(Name)>
+        argname
+      </Styled(Name)>
+    </span>
+  </Group>
 </div>
 `;
diff --git a/src/client/rsg-components/Argument/index.js b/src/client/rsg-components/Argument/index.js
deleted file mode 100644
index 9f7737dd..00000000
--- a/src/client/rsg-components/Argument/index.js
+++ /dev/null
@@ -1,1 +0,0 @@
-export { default } from 'rsg-components/Argument/ArgumentRenderer';
diff --git a/src/client/rsg-components/Argument/index.ts b/src/client/rsg-components/Argument/index.ts
new file mode 100644
index 00000000..7de9b7bf
--- /dev/null
+++ b/src/client/rsg-components/Argument/index.ts
@@ -0,0 +1,1 @@
+export { default, ArgumentProps } from 'rsg-components/Argument/ArgumentRenderer';
diff --git a/src/client/rsg-components/Arguments/Arguments.spec.js b/src/client/rsg-components/Arguments/Arguments.spec.tsx
similarity index 90%
rename from src/client/rsg-components/Arguments/Arguments.spec.js
rename to src/client/rsg-components/Arguments/Arguments.spec.tsx
index 9f9a12d4..7ab2a034 100644
--- a/src/client/rsg-components/Arguments/Arguments.spec.js
+++ b/src/client/rsg-components/Arguments/Arguments.spec.tsx
@@ -1,5 +1,7 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import { ArgumentsRenderer, styles } from './ArgumentsRenderer';
+import classes from '../../../../test/classes';
 
 const props = {
 	classes: classes(styles),
diff --git a/src/client/rsg-components/Arguments/ArgumentsRenderer.js b/src/client/rsg-components/Arguments/ArgumentsRenderer.tsx
similarity index 57%
rename from src/client/rsg-components/Arguments/ArgumentsRenderer.js
rename to src/client/rsg-components/Arguments/ArgumentsRenderer.tsx
index 4d67388d..fe358c35 100644
--- a/src/client/rsg-components/Arguments/ArgumentsRenderer.js
+++ b/src/client/rsg-components/Arguments/ArgumentsRenderer.tsx
@@ -1,10 +1,10 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import Argument from 'rsg-components/Argument';
+import Styled, { Theme, JssInjectedProps } from 'rsg-components/Styled';
+import Argument, { ArgumentProps } from 'rsg-components/Argument';
 import Heading from 'rsg-components/Heading';
-import Styled from 'rsg-components/Styled';
 
-export const styles = ({ space }) => ({
+export const styles = ({ space }: Theme) => ({
 	root: {
 		marginBottom: space[2],
 		fontSize: 'inherit',
@@ -14,7 +14,16 @@ export const styles = ({ space }) => ({
 	},
 });
 
-export function ArgumentsRenderer({ classes, args, heading }) {
+interface ArgumentsProps extends JssInjectedProps {
+	heading?: boolean;
+	args: ArgumentProps[];
+}
+
+export const ArgumentsRenderer: React.FunctionComponent<ArgumentsProps> = ({
+	classes,
+	args,
+	heading,
+}) => {
 	if (args.length === 0) {
 		return null;
 	}
@@ -31,18 +40,18 @@ export function ArgumentsRenderer({ classes, args, heading }) {
 			))}
 		</div>
 	);
-}
+};
 
 ArgumentsRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	args: PropTypes.arrayOf(
 		PropTypes.shape({
 			name: PropTypes.string.isRequired,
 			type: PropTypes.object,
 			description: PropTypes.string,
-		})
+		}).isRequired
 	).isRequired,
 	heading: PropTypes.bool,
 };
 
-export default Styled(styles)(ArgumentsRenderer);
+export default Styled<ArgumentsProps>(styles)(ArgumentsRenderer);
diff --git a/src/client/rsg-components/Arguments/__snapshots__/Arguments.spec.js.snap b/src/client/rsg-components/Arguments/__snapshots__/Arguments.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Arguments/__snapshots__/Arguments.spec.js.snap
rename to src/client/rsg-components/Arguments/__snapshots__/Arguments.spec.tsx.snap
diff --git a/src/client/rsg-components/Arguments/index.js b/src/client/rsg-components/Arguments/index.ts
similarity index 100%
rename from src/client/rsg-components/Arguments/index.js
rename to src/client/rsg-components/Arguments/index.ts
diff --git a/src/client/rsg-components/Code/Code.spec.js b/src/client/rsg-components/Code/Code.spec.tsx
similarity index 89%
rename from src/client/rsg-components/Code/Code.spec.js
rename to src/client/rsg-components/Code/Code.spec.tsx
index a1425ca9..a9f2b440 100644
--- a/src/client/rsg-components/Code/Code.spec.js
+++ b/src/client/rsg-components/Code/Code.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import { CodeRenderer } from './CodeRenderer';
 
 describe('Code blocks', () => {
diff --git a/src/client/rsg-components/Code/CodeRenderer.js b/src/client/rsg-components/Code/CodeRenderer.js
deleted file mode 100644
index 0b737ba4..00000000
--- a/src/client/rsg-components/Code/CodeRenderer.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
-
-const styles = ({ fontFamily }) => ({
-	code: {
-		fontFamily: fontFamily.monospace,
-		fontSize: 'inherit',
-		color: 'inherit',
-		background: 'transparent',
-		whiteSpace: 'inherit',
-	},
-});
-
-export function CodeRenderer({ classes, children }) {
-	return <code className={classes.code}>{children}</code>;
-}
-CodeRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
-	children: PropTypes.node.isRequired,
-};
-
-export default Styled(styles)(CodeRenderer);
diff --git a/src/client/rsg-components/Code/CodeRenderer.tsx b/src/client/rsg-components/Code/CodeRenderer.tsx
new file mode 100644
index 00000000..a14dda85
--- /dev/null
+++ b/src/client/rsg-components/Code/CodeRenderer.tsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Styled, { Theme, JssInjectedProps } from 'rsg-components/Styled';
+
+const styles = ({ fontFamily }: Theme) => ({
+	code: {
+		fontFamily: fontFamily.monospace,
+		fontSize: 'inherit',
+		color: 'inherit',
+		background: 'transparent',
+		whiteSpace: 'inherit',
+	},
+});
+
+interface CodeProps extends JssInjectedProps {
+	children: React.ReactNode;
+}
+
+export const CodeRenderer: React.FunctionComponent<CodeProps> = ({ classes, children }) => {
+	return <code className={classes.code}>{children}</code>;
+};
+CodeRenderer.propTypes = {
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
+	children: PropTypes.node.isRequired,
+};
+
+export default Styled<CodeProps>(styles)(CodeRenderer);
diff --git a/src/client/rsg-components/Code/__snapshots__/Code.spec.js.snap b/src/client/rsg-components/Code/__snapshots__/Code.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Code/__snapshots__/Code.spec.js.snap
rename to src/client/rsg-components/Code/__snapshots__/Code.spec.tsx.snap
diff --git a/src/client/rsg-components/Code/index.js b/src/client/rsg-components/Code/index.ts
similarity index 100%
rename from src/client/rsg-components/Code/index.js
rename to src/client/rsg-components/Code/index.ts
diff --git a/src/client/rsg-components/Components/Components.spec.js b/src/client/rsg-components/Components/Components.spec.tsx
similarity index 97%
rename from src/client/rsg-components/Components/Components.spec.js
rename to src/client/rsg-components/Components/Components.spec.tsx
index 68583f52..2c63a514 100644
--- a/src/client/rsg-components/Components/Components.spec.js
+++ b/src/client/rsg-components/Components/Components.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import ReactComponent from '../ReactComponent';
 import Components from './Components';
 import ComponentsRenderer from './ComponentsRenderer';
diff --git a/src/client/rsg-components/Components/Components.js b/src/client/rsg-components/Components/Components.tsx
similarity index 71%
rename from src/client/rsg-components/Components/Components.js
rename to src/client/rsg-components/Components/Components.tsx
index 67cf87a8..15280dd4 100644
--- a/src/client/rsg-components/Components/Components.js
+++ b/src/client/rsg-components/Components/Components.tsx
@@ -3,7 +3,19 @@ import PropTypes from 'prop-types';
 import ReactComponent from 'rsg-components/ReactComponent';
 import ComponentsRenderer from 'rsg-components/Components/ComponentsRenderer';
 
-export default function Components({ components, depth, exampleMode, usageMode }) {
+interface ComponentsProps {
+	components: any[];
+	depth: number;
+	exampleMode?: string;
+	usageMode?: string;
+}
+
+const Components: React.FunctionComponent<ComponentsProps> = ({
+	components,
+	depth,
+	exampleMode,
+	usageMode,
+}) => {
 	return (
 		<ComponentsRenderer>
 			{components.map(component => (
@@ -17,7 +29,7 @@ export default function Components({ components, depth, exampleMode, usageMode }
 			))}
 		</ComponentsRenderer>
 	);
-}
+};
 
 Components.propTypes = {
 	components: PropTypes.array.isRequired,
@@ -25,3 +37,5 @@ Components.propTypes = {
 	exampleMode: PropTypes.string.isRequired,
 	usageMode: PropTypes.string.isRequired,
 };
+
+export default Components;
diff --git a/src/client/rsg-components/Components/ComponentsRenderer.js b/src/client/rsg-components/Components/ComponentsRenderer.tsx
similarity index 65%
rename from src/client/rsg-components/Components/ComponentsRenderer.js
rename to src/client/rsg-components/Components/ComponentsRenderer.tsx
index e2e9abcb..8cfb75a5 100644
--- a/src/client/rsg-components/Components/ComponentsRenderer.js
+++ b/src/client/rsg-components/Components/ComponentsRenderer.tsx
@@ -1,7 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 
-export default function ComponentsRenderer({ children }) {
+export default function ComponentsRenderer({ children }: { children: React.ReactNode }) {
 	return <div>{children}</div>;
 }
 ComponentsRenderer.propTypes = {
diff --git a/src/client/rsg-components/Components/__snapshots__/Components.spec.js.snap b/src/client/rsg-components/Components/__snapshots__/Components.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Components/__snapshots__/Components.spec.js.snap
rename to src/client/rsg-components/Components/__snapshots__/Components.spec.tsx.snap
diff --git a/src/client/rsg-components/Components/index.js b/src/client/rsg-components/Components/index.ts
similarity index 100%
rename from src/client/rsg-components/Components/index.js
rename to src/client/rsg-components/Components/index.ts
diff --git a/src/client/rsg-components/ComponentsList/ComponentsList.spec.js b/src/client/rsg-components/ComponentsList/ComponentsList.spec.tsx
similarity index 75%
rename from src/client/rsg-components/ComponentsList/ComponentsList.spec.js
rename to src/client/rsg-components/ComponentsList/ComponentsList.spec.tsx
index ccbd3de1..bd293997 100644
--- a/src/client/rsg-components/ComponentsList/ComponentsList.spec.js
+++ b/src/client/rsg-components/ComponentsList/ComponentsList.spec.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable compat/compat */
 import React from 'react';
 import { render } from '@testing-library/react';
 import ComponentsList from './ComponentsList';
@@ -9,7 +10,7 @@ const context = {
 	},
 };
 
-const Provider = props => <Context.Provider value={context} {...props} />;
+const Provider = (props: any) => <Context.Provider value={context} {...props} />;
 
 it('should set the correct href for items', () => {
 	const components = [
@@ -27,11 +28,11 @@ it('should set the correct href for items', () => {
 
 	const { getAllByRole } = render(
 		<Provider>
-			<ComponentsList items={components} classes={{}} />
+			<ComponentsList items={components} />
 		</Provider>
 	);
 
-	expect(Array.from(getAllByRole('link')).map(node => node.href)).toEqual([
+	expect(Array.from(getAllByRole('link')).map(node => (node as HTMLAnchorElement).href)).toEqual([
 		'http://localhost/#button',
 		'http://localhost/#input',
 	]);
@@ -53,11 +54,11 @@ it('if a custom href is provided, should use it instead of generating internal l
 
 	const { getAllByRole } = render(
 		<Provider>
-			<ComponentsList items={components} classes={{}} />
+			<ComponentsList items={components} />
 		</Provider>
 	);
 
-	expect(Array.from(getAllByRole('link')).map(node => node.href)).toEqual([
+	expect(Array.from(getAllByRole('link')).map(node => (node as HTMLAnchorElement).href)).toEqual([
 		'http://example.com/',
 		'http://localhost/#input',
 	]);
@@ -79,17 +80,11 @@ it('should set an id parameter on link when useHashId is activated', () => {
 
 	const { getAllByRole } = render(
 		<Provider>
-			<ComponentsList
-				items={components}
-				classes={{}}
-				useRouterLinks
-				hashPath={['Components']}
-				useHashId
-			/>
+			<ComponentsList items={components} useRouterLinks hashPath={['Components']} useHashId />
 		</Provider>
 	);
 
-	expect(Array.from(getAllByRole('link')).map(node => node.href)).toEqual([
+	expect(Array.from(getAllByRole('link')).map(node => (node as HTMLAnchorElement).href)).toEqual([
 		'http://localhost/#/Components?id=button',
 		'http://localhost/#/Components?id=input',
 	]);
@@ -113,7 +108,6 @@ it('should set a sub route on link when useHashId is deactivated', () => {
 		<Provider>
 			<ComponentsList
 				items={components}
-				classes={{}}
 				useRouterLinks
 				hashPath={['Components']}
 				useHashId={false}
@@ -121,7 +115,7 @@ it('should set a sub route on link when useHashId is deactivated', () => {
 		</Provider>
 	);
 
-	expect(Array.from(getAllByRole('link')).map(node => node.href)).toEqual([
+	expect(Array.from(getAllByRole('link')).map(node => (node as HTMLAnchorElement).href)).toEqual([
 		'http://localhost/#/Components/Button',
 		'http://localhost/#/Components/Input',
 	]);
@@ -130,7 +124,7 @@ it('should set a sub route on link when useHashId is deactivated', () => {
 it('should not render any links when the list is empty', () => {
 	const { queryAllByRole } = render(
 		<Provider>
-			<ComponentsList items={[]} classes={{}} />
+			<ComponentsList items={[]} />
 		</Provider>
 	);
 
@@ -154,7 +148,6 @@ it('should ignore items without visibleName', () => {
 		<Provider>
 			<ComponentsList
 				items={components}
-				classes={{}}
 				useRouterLinks
 				hashPath={['Components']}
 				useHashId={false}
@@ -162,7 +155,7 @@ it('should ignore items without visibleName', () => {
 		</Provider>
 	);
 
-	expect(Array.from(getAllByRole('link')).map(node => node.href)).toEqual([
+	expect(Array.from(getAllByRole('link')).map(node => (node as HTMLAnchorElement).href)).toEqual([
 		'http://localhost/#button',
 	]);
 });
diff --git a/src/client/rsg-components/ComponentsList/ComponentsList.js b/src/client/rsg-components/ComponentsList/ComponentsList.tsx
similarity index 64%
rename from src/client/rsg-components/ComponentsList/ComponentsList.js
rename to src/client/rsg-components/ComponentsList/ComponentsList.tsx
index ef471e5b..6b5e291e 100644
--- a/src/client/rsg-components/ComponentsList/ComponentsList.js
+++ b/src/client/rsg-components/ComponentsList/ComponentsList.tsx
@@ -1,9 +1,22 @@
 import React from 'react';
-import ComponentsListRenderer from 'rsg-components/ComponentsList/ComponentsListRenderer';
 import PropTypes from 'prop-types';
+import ComponentsListRenderer from 'rsg-components/ComponentsList/ComponentsListRenderer';
+import { ComponentViewModel } from 'rsg-components/ReactComponent';
 import getUrl from '../../utils/getUrl';
 
-function ComponentsList({ classes, items, useRouterLinks = false, useHashId, hashPath }) {
+interface ComponentsListProps {
+	items: ComponentViewModel[];
+	hashPath?: string[];
+	useRouterLinks?: boolean;
+	useHashId?: boolean;
+}
+
+const ComponentsList: React.FunctionComponent<ComponentsListProps> = ({
+	items,
+	useRouterLinks = false,
+	useHashId,
+	hashPath,
+}) => {
 	const mappedItems = items.map(item => ({
 		...item,
 		shouldOpenInNewTab: !!item.href,
@@ -17,12 +30,11 @@ function ComponentsList({ classes, items, useRouterLinks = false, useHashId, has
 					id: useRouterLinks ? useHashId : false,
 			  }),
 	}));
-	return <ComponentsListRenderer classes={classes} items={mappedItems} />;
-}
+	return <ComponentsListRenderer items={mappedItems} />;
+};
 
 ComponentsList.propTypes = {
 	items: PropTypes.array.isRequired,
-	classes: PropTypes.object,
 	hashPath: PropTypes.array,
 	useRouterLinks: PropTypes.bool,
 	useHashId: PropTypes.bool,
diff --git a/src/client/rsg-components/ComponentsList/ComponentsListRenderer.js b/src/client/rsg-components/ComponentsList/ComponentsListRenderer.tsx
similarity index 76%
rename from src/client/rsg-components/ComponentsList/ComponentsListRenderer.js
rename to src/client/rsg-components/ComponentsList/ComponentsListRenderer.tsx
index 3097738c..7f9aa2d5 100644
--- a/src/client/rsg-components/ComponentsList/ComponentsListRenderer.js
+++ b/src/client/rsg-components/ComponentsList/ComponentsListRenderer.tsx
@@ -2,11 +2,12 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import cx from 'clsx';
 import Link from 'rsg-components/Link';
-import Styled from 'rsg-components/Styled';
+import Styled, { Theme, JssInjectedProps } from 'rsg-components/Styled';
 import { useStyleGuideContext } from 'rsg-components/Context';
+import { ComponentViewModel } from 'rsg-components/ReactComponent';
 import { getHash } from '../../utils/handleHash';
 
-const styles = ({ color, fontFamily, fontSize, space, mq }) => ({
+const styles = ({ color, fontFamily, fontSize, space, mq }: Theme) => ({
 	list: {
 		margin: 0,
 		paddingLeft: space[2],
@@ -38,7 +39,14 @@ const styles = ({ color, fontFamily, fontSize, space, mq }) => ({
 	},
 });
 
-export function ComponentsListRenderer({ classes, items }) {
+interface ComponentsListRendererProps extends JssInjectedProps {
+	items: ComponentViewModel[];
+}
+
+export const ComponentsListRenderer: React.FunctionComponent<ComponentsListRendererProps> = ({
+	classes,
+	items,
+}) => {
 	const {
 		config: { pagePerSection },
 	} = useStyleGuideContext();
@@ -77,11 +85,11 @@ export function ComponentsListRenderer({ classes, items }) {
 			})}
 		</ul>
 	);
-}
+};
 
 ComponentsListRenderer.propTypes = {
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	items: PropTypes.array.isRequired,
-	classes: PropTypes.object.isRequired,
 };
 
-export default Styled(styles)(ComponentsListRenderer);
+export default Styled<ComponentsListRendererProps>(styles)(ComponentsListRenderer);
diff --git a/src/client/rsg-components/ComponentsList/index.js b/src/client/rsg-components/ComponentsList/index.ts
similarity index 53%
rename from src/client/rsg-components/ComponentsList/index.js
rename to src/client/rsg-components/ComponentsList/index.ts
index 564ab475..a31fea9d 100644
--- a/src/client/rsg-components/ComponentsList/index.js
+++ b/src/client/rsg-components/ComponentsList/index.ts
@@ -1,1 +1,2 @@
 export { default } from 'rsg-components/ComponentsList/ComponentsList';
+export * from 'rsg-components/ComponentsList/ComponentsList';
diff --git a/src/client/rsg-components/Context/Context.ts b/src/client/rsg-components/Context/Context.ts
index 630acaee..97d2dce6 100644
--- a/src/client/rsg-components/Context/Context.ts
+++ b/src/client/rsg-components/Context/Context.ts
@@ -1,9 +1,27 @@
 import React from 'react';
+import { ProcessedStyleguidistConfig } from '../../../scripts/schemas/config';
 
-const StyleGuideContext = React.createContext({});
+const StyleGuideContext = React.createContext<StyleGuideContextContents>({
+	codeRevision: 0,
+	config: {} as ProcessedStyleguidistConfig,
+	slots: {},
+	displayMode: 'collapse',
+});
 
 export default StyleGuideContext;
 
-export function useStyleGuideContext() {
+export interface SlotObject {
+	id: string;
+	render: React.FunctionComponent<any>;
+}
+
+export interface StyleGuideContextContents {
+	codeRevision: number;
+	config: ProcessedStyleguidistConfig;
+	slots: Record<string, (SlotObject | React.FunctionComponent<any>)[]>;
+	displayMode: string;
+}
+
+export function useStyleGuideContext(): StyleGuideContextContents {
 	return React.useContext(StyleGuideContext);
 }
diff --git a/src/client/rsg-components/Context/index.ts b/src/client/rsg-components/Context/index.ts
index 317f2aea..0ba8786b 100644
--- a/src/client/rsg-components/Context/index.ts
+++ b/src/client/rsg-components/Context/index.ts
@@ -1,2 +1,2 @@
 export { default } from 'rsg-components/Context/Context';
-export { useStyleGuideContext } from 'rsg-components/Context/Context';
+export * from 'rsg-components/Context/Context';
diff --git a/src/client/rsg-components/Editor/Editor.spec.js b/src/client/rsg-components/Editor/Editor.spec.tsx
similarity index 95%
rename from src/client/rsg-components/Editor/Editor.spec.js
rename to src/client/rsg-components/Editor/Editor.spec.tsx
index d4793199..4ce518e7 100644
--- a/src/client/rsg-components/Editor/Editor.spec.js
+++ b/src/client/rsg-components/Editor/Editor.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { shallow, mount } from 'enzyme';
 import { Editor } from './Editor';
 
 const code = '<button>MyAwesomeCode</button>';
diff --git a/src/client/rsg-components/Editor/Editor.js b/src/client/rsg-components/Editor/Editor.tsx
similarity index 61%
rename from src/client/rsg-components/Editor/Editor.js
rename to src/client/rsg-components/Editor/Editor.tsx
index 5ff506d7..074c2453 100644
--- a/src/client/rsg-components/Editor/Editor.js
+++ b/src/client/rsg-components/Editor/Editor.tsx
@@ -1,8 +1,8 @@
 import React, { Component } from 'react';
 import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
+import Styled, { Theme, JssInjectedProps } from 'rsg-components/Styled';
 import SimpleEditor from 'react-simple-code-editor';
-import Prism from 'prismjs/components/prism-core';
+import { highlight as prismHighlight, languages } from 'prismjs';
 import 'prismjs/components/prism-clike';
 import 'prismjs/components/prism-markup';
 import 'prismjs/components/prism-javascript';
@@ -10,9 +10,9 @@ import 'prismjs/components/prism-jsx';
 import { space } from '../../styles/theme';
 import prismTheme from '../../styles/prismTheme';
 
-const highlight = code => Prism.highlight(code, Prism.languages.jsx, 'jsx');
+const highlight = (code: string) => prismHighlight(code, languages.jsx, 'jsx');
 
-const styles = ({ fontFamily, fontSize, color, borderRadius }) => ({
+const styles = ({ fontFamily, fontSize, color, borderRadius }: Theme) => ({
 	root: {
 		fontFamily: fontFamily.monospace,
 		fontSize: fontSize.small,
@@ -35,16 +35,26 @@ const styles = ({ fontFamily, fontSize, color, borderRadius }) => ({
 	},
 });
 
-export class Editor extends Component {
-	static propTypes = {
+interface EditorProps extends JssInjectedProps {
+	code: string;
+	onChange: (code: string) => void;
+}
+
+interface EditorState {
+	code: string;
+	prevCode: string;
+}
+
+export class Editor extends Component<EditorProps> {
+	public static propTypes = {
 		code: PropTypes.string.isRequired,
 		onChange: PropTypes.func.isRequired,
-		classes: PropTypes.object.isRequired,
+		classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	};
 
-	state = { code: this.props.code, prevCode: this.props.code };
+	public state = { code: this.props.code, prevCode: this.props.code };
 
-	static getDerivedStateFromProps(nextProps, prevState) {
+	public static getDerivedStateFromProps(nextProps: EditorProps, prevState: EditorState) {
 		const { code } = nextProps;
 		if (prevState.prevCode !== code) {
 			return {
@@ -55,16 +65,16 @@ export class Editor extends Component {
 		return null;
 	}
 
-	shouldComponentUpdate(nextProps, nextState) {
+	public shouldComponentUpdate(nextProps: EditorProps, nextState: EditorState) {
 		return nextState.code !== this.state.code;
 	}
 
-	handleChange = code => {
+	private handleChange = (code: string) => {
 		this.setState({ code });
 		this.props.onChange(code);
 	};
 
-	render() {
+	public render() {
 		return (
 			<SimpleEditor
 				className={this.props.classes.root}
@@ -79,4 +89,4 @@ export class Editor extends Component {
 	}
 }
 
-export default Styled(styles)(Editor);
+export default Styled<EditorProps>(styles)(Editor);
diff --git a/src/client/rsg-components/Editor/__snapshots__/Editor.spec.js.snap b/src/client/rsg-components/Editor/__snapshots__/Editor.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Editor/__snapshots__/Editor.spec.js.snap
rename to src/client/rsg-components/Editor/__snapshots__/Editor.spec.tsx.snap
diff --git a/src/client/rsg-components/Editor/index.js b/src/client/rsg-components/Editor/index.ts
similarity index 100%
rename from src/client/rsg-components/Editor/index.js
rename to src/client/rsg-components/Editor/index.ts
diff --git a/src/client/rsg-components/Error/Error.spec.js b/src/client/rsg-components/Error/Error.spec.tsx
similarity index 79%
rename from src/client/rsg-components/Error/Error.spec.js
rename to src/client/rsg-components/Error/Error.spec.tsx
index 6c35b1c6..a15f3ffd 100644
--- a/src/client/rsg-components/Error/Error.spec.js
+++ b/src/client/rsg-components/Error/Error.spec.tsx
@@ -1,9 +1,10 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import { ErrorRenderer } from './ErrorRenderer';
 
 it('renderer should render error message', () => {
 	const error = { toString: () => 'error' };
-	const info = { componentStack: { toString: () => 'info' } };
+	const info = { componentStack: 'info' };
 	const actual = shallow(<ErrorRenderer classes={{}} error={error} info={info} />);
 
 	expect(actual).toMatchSnapshot();
diff --git a/src/client/rsg-components/Error/ErrorRenderer.js b/src/client/rsg-components/Error/ErrorRenderer.tsx
similarity index 63%
rename from src/client/rsg-components/Error/ErrorRenderer.js
rename to src/client/rsg-components/Error/ErrorRenderer.tsx
index 4e8acfef..017236c9 100644
--- a/src/client/rsg-components/Error/ErrorRenderer.js
+++ b/src/client/rsg-components/Error/ErrorRenderer.tsx
@@ -1,8 +1,8 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
+import Styled, { Theme, JssInjectedProps } from 'rsg-components/Styled';
 
-const styles = ({ fontFamily, fontSize, color, space }) => ({
+const styles = ({ fontFamily, fontSize, color, space }: Theme) => ({
 	root: {
 		margin: space[2],
 		lineHeight: 1.2,
@@ -19,12 +19,17 @@ const styles = ({ fontFamily, fontSize, color, space }) => ({
 	},
 });
 
-export function ErrorRenderer({ classes, error, info }) {
+interface ErrorProps extends JssInjectedProps {
+	error: any;
+	info: React.ErrorInfo;
+}
+
+export const ErrorRenderer: React.FunctionComponent<ErrorProps> = ({ classes, error, info }) => {
 	return (
 		<div className={classes.root}>
 			<pre className={classes.stack}>
 				{error.toString()}
-				{info.componentStack.toString()}
+				{info.componentStack}
 			</pre>
 			<div className={classes.message}>
 				<p>
@@ -44,14 +49,12 @@ export function ErrorRenderer({ classes, error, info }) {
 			</div>
 		</div>
 	);
-}
+};
 
 ErrorRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	error: PropTypes.object.isRequired,
-	info: PropTypes.shape({
-		componentStack: PropTypes.any.isRequired,
-	}).isRequired,
+	info: PropTypes.any.isRequired,
 };
 
-export default Styled(styles)(ErrorRenderer);
+export default Styled<ErrorProps>(styles)(ErrorRenderer);
diff --git a/src/client/rsg-components/Error/__snapshots__/Error.spec.js.snap b/src/client/rsg-components/Error/__snapshots__/Error.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Error/__snapshots__/Error.spec.js.snap
rename to src/client/rsg-components/Error/__snapshots__/Error.spec.tsx.snap
diff --git a/src/client/rsg-components/Error/index.js b/src/client/rsg-components/Error/index.ts
similarity index 100%
rename from src/client/rsg-components/Error/index.js
rename to src/client/rsg-components/Error/index.ts
diff --git a/src/client/rsg-components/ExamplePlaceholder/ExamplePlaceholder.spec.js b/src/client/rsg-components/ExamplePlaceholder/ExamplePlaceholder.spec.tsx
similarity index 100%
rename from src/client/rsg-components/ExamplePlaceholder/ExamplePlaceholder.spec.js
rename to src/client/rsg-components/ExamplePlaceholder/ExamplePlaceholder.spec.tsx
diff --git a/src/client/rsg-components/ExamplePlaceholder/ExamplePlaceholderRenderer.js b/src/client/rsg-components/ExamplePlaceholder/ExamplePlaceholderRenderer.tsx
similarity index 68%
rename from src/client/rsg-components/ExamplePlaceholder/ExamplePlaceholderRenderer.js
rename to src/client/rsg-components/ExamplePlaceholder/ExamplePlaceholderRenderer.tsx
index 0ff17cb7..acd01783 100644
--- a/src/client/rsg-components/ExamplePlaceholder/ExamplePlaceholderRenderer.js
+++ b/src/client/rsg-components/ExamplePlaceholder/ExamplePlaceholderRenderer.tsx
@@ -1,10 +1,10 @@
 import React, { Component } from 'react';
 import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
+import Styled, { Theme, JssInjectedProps } from 'rsg-components/Styled';
 import Markdown from 'rsg-components/Markdown';
 import { DOCS_DOCUMENTING } from '../../../scripts/consts';
 
-const styles = ({ fontFamily, fontSize, color }) => ({
+const styles = ({ fontFamily, fontSize, color }: Theme) => ({
 	button: {
 		padding: 0,
 		fontSize: fontSize.base,
@@ -21,21 +21,25 @@ const styles = ({ fontFamily, fontSize, color }) => ({
 	},
 });
 
-export class ExamplePlaceholderRenderer extends Component {
-	static propTypes = {
-		classes: PropTypes.object.isRequired,
+interface ExamplePlaceholderProps extends JssInjectedProps {
+	name?: string;
+}
+
+export class ExamplePlaceholderRenderer extends Component<ExamplePlaceholderProps> {
+	public static propTypes = {
+		classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 		name: PropTypes.string,
 	};
 
-	state = {
+	public state = {
 		isVisible: false,
 	};
 
-	handleOpen = () => {
+	public handleOpen = () => {
 		this.setState({ isVisible: true });
 	};
 
-	render() {
+	public render() {
 		const { classes, name } = this.props;
 		const { isVisible } = this.state;
 		if (isVisible) {
@@ -66,4 +70,4 @@ Read more in the [documenting components guide](${DOCS_DOCUMENTING}).
 	}
 }
 
-export default Styled(styles)(ExamplePlaceholderRenderer);
+export default Styled<ExamplePlaceholderProps>(styles)(ExamplePlaceholderRenderer);
diff --git a/src/client/rsg-components/ExamplePlaceholder/index.js b/src/client/rsg-components/ExamplePlaceholder/index.ts
similarity index 100%
rename from src/client/rsg-components/ExamplePlaceholder/index.js
rename to src/client/rsg-components/ExamplePlaceholder/index.ts
diff --git a/src/client/rsg-components/Examples/Examples.spec.js b/src/client/rsg-components/Examples/Examples.spec.tsx
similarity index 84%
rename from src/client/rsg-components/Examples/Examples.spec.js
rename to src/client/rsg-components/Examples/Examples.spec.tsx
index 0f5805d3..75b3fea0 100644
--- a/src/client/rsg-components/Examples/Examples.spec.js
+++ b/src/client/rsg-components/Examples/Examples.spec.tsx
@@ -1,15 +1,15 @@
 import React from 'react';
 import { render } from '@testing-library/react';
-import Examples from '../Examples';
+import Examples, { ExampleModel } from '.';
 import Context from '../Context';
 import slots from '../slots';
 import { DisplayModes } from '../../consts';
 
-const evalInContext = a =>
+const evalInContext = (a: string): (() => any) =>
 	// eslint-disable-next-line no-new-func
 	new Function('require', 'const React = require("react");' + a).bind(null, require);
 
-const examples = [
+const examples: ExampleModel[] = [
 	{
 		type: 'code',
 		content: '<button>Code: OK</button>',
@@ -30,7 +30,7 @@ const context = {
 	slots: slots(),
 };
 
-const Provider = props => <Context.Provider value={context} {...props} />;
+const Provider = (props: any) => <Context.Provider value={context} {...props} />;
 
 test('should render examples', () => {
 	const { getByText } = render(
@@ -47,7 +47,7 @@ test('should not render an example with unknown type', () => {
 		{
 			type: 'unknown',
 			content: 'FooBar',
-		},
+		} as any,
 	];
 	const { getByTestId } = render(
 		<Provider>
diff --git a/src/client/rsg-components/Examples/Examples.js b/src/client/rsg-components/Examples/Examples.tsx
similarity index 66%
rename from src/client/rsg-components/Examples/Examples.js
rename to src/client/rsg-components/Examples/Examples.tsx
index 1db46637..b43dc1e2 100644
--- a/src/client/rsg-components/Examples/Examples.js
+++ b/src/client/rsg-components/Examples/Examples.tsx
@@ -5,7 +5,28 @@ import Markdown from 'rsg-components/Markdown';
 import ExamplesRenderer from 'rsg-components/Examples/ExamplesRenderer';
 import { useStyleGuideContext } from 'rsg-components/Context';
 
-export default function Examples({ examples, name, exampleMode }) {
+interface ExampleModelMarkdown {
+	type: 'markdown';
+	content: string;
+	settings?: Record<string, any>;
+}
+
+interface ExampleModelCode {
+	evalInContext(a: string): () => any;
+	type: 'code';
+	content: string;
+	settings?: Record<string, any>;
+}
+
+export type ExampleModel = ExampleModelCode | ExampleModelMarkdown;
+
+interface ExamplesRenderer {
+	examples: ExampleModel[];
+	name?: string;
+	exampleMode?: string;
+}
+
+const Examples: React.FunctionComponent<ExamplesRenderer> = ({ examples, name, exampleMode }) => {
 	const { codeRevision } = useStyleGuideContext();
 	return (
 		<ExamplesRenderer name={name}>
@@ -31,10 +52,12 @@ export default function Examples({ examples, name, exampleMode }) {
 			})}
 		</ExamplesRenderer>
 	);
-}
+};
 
 Examples.propTypes = {
 	examples: PropTypes.array.isRequired,
 	name: PropTypes.string.isRequired,
 	exampleMode: PropTypes.string.isRequired,
 };
+
+export default Examples;
diff --git a/src/client/rsg-components/Examples/ExamplesRenderer.js b/src/client/rsg-components/Examples/ExamplesRenderer.js
deleted file mode 100644
index 44301610..00000000
--- a/src/client/rsg-components/Examples/ExamplesRenderer.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
-
-const styles = () => ({
-	// Just default jss-isolate rules
-	root: {},
-});
-
-export function ExamplesRenderer({ classes, name, children }) {
-	return (
-		<article className={classes.root} data-testid={`${name}-examples`}>
-			{children}
-		</article>
-	);
-}
-
-ExamplesRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
-	name: PropTypes.string.isRequired,
-	children: PropTypes.node,
-};
-
-export default Styled(styles)(ExamplesRenderer);
diff --git a/src/client/rsg-components/Examples/ExamplesRenderer.tsx b/src/client/rsg-components/Examples/ExamplesRenderer.tsx
new file mode 100644
index 00000000..366b026d
--- /dev/null
+++ b/src/client/rsg-components/Examples/ExamplesRenderer.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Styled, { JssInjectedProps } from 'rsg-components/Styled';
+
+const styles = () => ({
+	// Just default jss-isolate rules
+	root: {},
+});
+
+interface ExamplesRendererProps extends JssInjectedProps {
+	children?: React.ReactNode;
+	name?: string;
+}
+
+export const ExamplesRenderer: React.FunctionComponent<ExamplesRendererProps> = ({
+	classes,
+	name,
+	children,
+}) => {
+	return (
+		<article className={classes.root} data-testid={`${name}-examples`}>
+			{children}
+		</article>
+	);
+};
+
+ExamplesRenderer.propTypes = {
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
+	name: PropTypes.string.isRequired,
+	children: PropTypes.node,
+};
+
+export default Styled<ExamplesRendererProps>(styles)(ExamplesRenderer);
diff --git a/src/client/rsg-components/Examples/index.js b/src/client/rsg-components/Examples/index.ts
similarity index 54%
rename from src/client/rsg-components/Examples/index.js
rename to src/client/rsg-components/Examples/index.ts
index 94676c43..5bae173d 100644
--- a/src/client/rsg-components/Examples/index.js
+++ b/src/client/rsg-components/Examples/index.ts
@@ -1,1 +1,2 @@
 export { default } from 'rsg-components/Examples/Examples';
+export * from 'rsg-components/Examples/Examples';
diff --git a/src/client/rsg-components/Heading/Heading.spec.js b/src/client/rsg-components/Heading/Heading.spec.tsx
similarity index 93%
rename from src/client/rsg-components/Heading/Heading.spec.js
rename to src/client/rsg-components/Heading/Heading.spec.tsx
index 362acba1..9a8481d3 100644
--- a/src/client/rsg-components/Heading/Heading.spec.js
+++ b/src/client/rsg-components/Heading/Heading.spec.tsx
@@ -1,5 +1,6 @@
 import React from 'react';
 import { html } from 'cheerio';
+import { render, shallow } from 'enzyme';
 import Heading from './index';
 
 describe('Heading', () => {
diff --git a/src/client/rsg-components/Heading/HeadingRenderer.js b/src/client/rsg-components/Heading/HeadingRenderer.tsx
similarity index 56%
rename from src/client/rsg-components/Heading/HeadingRenderer.js
rename to src/client/rsg-components/Heading/HeadingRenderer.tsx
index 6227a1a3..54f823a7 100644
--- a/src/client/rsg-components/Heading/HeadingRenderer.js
+++ b/src/client/rsg-components/Heading/HeadingRenderer.tsx
@@ -1,9 +1,9 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import cx from 'clsx';
-import Styled from 'rsg-components/Styled';
+import Styled, { Theme, JssInjectedProps } from 'rsg-components/Styled';
 
-const styles = ({ color, fontFamily, fontSize }) => ({
+const styles = ({ color, fontFamily, fontSize }: Theme) => ({
 	heading: {
 		margin: 0,
 		color: color.base,
@@ -32,8 +32,18 @@ const styles = ({ color, fontFamily, fontSize }) => ({
 	},
 });
 
-function HeadingRenderer({ classes, level, children, ...props }) {
-	const Tag = `h${level}`;
+interface HeadingProps extends JssInjectedProps, React.HTMLAttributes<HTMLHeadingElement> {
+	children?: React.ReactNode;
+	level: number;
+}
+
+const HeadingRenderer: React.FunctionComponent<HeadingProps> = ({
+	classes,
+	level,
+	children,
+	...props
+}) => {
+	const Tag = `h${level}` as ('h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6');
 	const headingClasses = cx(classes.heading, classes[`heading${level}`]);
 
 	return (
@@ -41,12 +51,12 @@ function HeadingRenderer({ classes, level, children, ...props }) {
 			{children}
 		</Tag>
 	);
-}
+};
 
 HeadingRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	level: PropTypes.oneOf([1, 2, 3, 4, 5, 6]).isRequired,
 	children: PropTypes.node,
 };
 
-export default Styled(styles)(HeadingRenderer);
+export default Styled<HeadingProps>(styles)(HeadingRenderer);
diff --git a/src/client/rsg-components/Heading/__snapshots__/Heading.spec.js.snap b/src/client/rsg-components/Heading/__snapshots__/Heading.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Heading/__snapshots__/Heading.spec.js.snap
rename to src/client/rsg-components/Heading/__snapshots__/Heading.spec.tsx.snap
diff --git a/src/client/rsg-components/Heading/index.js b/src/client/rsg-components/Heading/index.ts
similarity index 100%
rename from src/client/rsg-components/Heading/index.js
rename to src/client/rsg-components/Heading/index.ts
diff --git a/src/client/rsg-components/JsDoc/JsDoc.js b/src/client/rsg-components/JsDoc/JsDoc.js
deleted file mode 100644
index 7c9aaab8..00000000
--- a/src/client/rsg-components/JsDoc/JsDoc.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Markdown from 'rsg-components/Markdown';
-import map from 'lodash/map';
-
-const plural = (array, caption) => (array.length === 1 ? caption : `${caption}s`);
-const list = array => array.map(item => item.description).join(', ');
-const paragraphs = array => array.map(item => item.description).join('\n\n');
-
-const fields = {
-	deprecated: value => `**Deprecated:** ${value[0].description}`,
-	see: value => paragraphs(value),
-	link: value => paragraphs(value),
-	author: value => `${plural(value, 'Author')}: ${list(value)}`,
-	version: value => `Version: ${value[0].description}`,
-	since: value => `Since: ${value[0].description}`,
-};
-
-export function getMarkdown(props) {
-	return map(fields, (format, field) => props[field] && format(props[field]))
-		.filter(Boolean)
-		.join('\n\n');
-}
-
-export default function JsDoc(props) {
-	const markdown = getMarkdown(props);
-	return markdown ? <Markdown text={markdown} /> : null;
-}
-
-JsDoc.propTypes = {
-	deprecated: PropTypes.array,
-	see: PropTypes.array,
-	link: PropTypes.array,
-	author: PropTypes.array,
-	version: PropTypes.array,
-	since: PropTypes.array,
-};
diff --git a/src/client/rsg-components/JsDoc/JsDoc.spec.js b/src/client/rsg-components/JsDoc/JsDoc.spec.tsx
similarity index 93%
rename from src/client/rsg-components/JsDoc/JsDoc.spec.js
rename to src/client/rsg-components/JsDoc/JsDoc.spec.tsx
index 8d5304f2..7cafbe15 100644
--- a/src/client/rsg-components/JsDoc/JsDoc.spec.js
+++ b/src/client/rsg-components/JsDoc/JsDoc.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import JsDoc, { getMarkdown } from './JsDoc';
 
 const tags = {
@@ -55,8 +56,9 @@ describe('getMarkdown', () => {
 	});
 
 	it('should return Markdown for one author', () => {
+		const author = tags.author ? [tags.author[0]] : undefined;
 		const result = getMarkdown({
-			author: [tags.author[0]],
+			author,
 		});
 		expect(result).toMatchSnapshot();
 	});
diff --git a/src/client/rsg-components/JsDoc/JsDoc.tsx b/src/client/rsg-components/JsDoc/JsDoc.tsx
new file mode 100644
index 00000000..430b52c7
--- /dev/null
+++ b/src/client/rsg-components/JsDoc/JsDoc.tsx
@@ -0,0 +1,41 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { TagProps, TagObject } from 'react-docgen';
+import map from 'lodash/map';
+import Markdown from 'rsg-components/Markdown';
+
+const plural = (array: any[], caption: string) => (array.length === 1 ? caption : `${caption}s`);
+const list = (array: TagObject[]) => array.map(item => item.description).join(', ');
+const paragraphs = (array: TagObject[]) => array.map(item => item.description).join('\n\n');
+
+const fields = {
+	deprecated: (value: TagObject[]) => `**Deprecated:** ${value[0].description}`,
+	see: (value: TagObject[]) => paragraphs(value),
+	link: (value: TagObject[]) => paragraphs(value),
+	author: (value: TagObject[]) => `${plural(value, 'Author')}: ${list(value)}`,
+	version: (value: TagObject[]) => `Version: ${value[0].description}`,
+	since: (value: TagObject[]) => `Since: ${value[0].description}`,
+};
+
+export function getMarkdown(props: TagProps) {
+	return map(fields, (format: (value: TagObject[]) => string, field: keyof TagProps) => {
+		const tag = props[field];
+		return tag && format(tag);
+	})
+		.filter(Boolean)
+		.join('\n\n');
+}
+
+export default function JsDoc(props: TagProps) {
+	const markdown = getMarkdown(props);
+	return markdown ? <Markdown text={markdown} /> : null;
+}
+
+JsDoc.propTypes = {
+	deprecated: PropTypes.array,
+	see: PropTypes.array,
+	link: PropTypes.array,
+	author: PropTypes.array,
+	version: PropTypes.array,
+	since: PropTypes.array,
+};
diff --git a/src/client/rsg-components/JsDoc/__snapshots__/JsDoc.spec.js.snap b/src/client/rsg-components/JsDoc/__snapshots__/JsDoc.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/JsDoc/__snapshots__/JsDoc.spec.js.snap
rename to src/client/rsg-components/JsDoc/__snapshots__/JsDoc.spec.tsx.snap
diff --git a/src/client/rsg-components/JsDoc/index.js b/src/client/rsg-components/JsDoc/index.ts
similarity index 100%
rename from src/client/rsg-components/JsDoc/index.js
rename to src/client/rsg-components/JsDoc/index.ts
diff --git a/src/client/rsg-components/Link/Link.spec.js b/src/client/rsg-components/Link/Link.spec.tsx
similarity index 96%
rename from src/client/rsg-components/Link/Link.spec.js
rename to src/client/rsg-components/Link/Link.spec.tsx
index 7f7a5c6d..be11b371 100644
--- a/src/client/rsg-components/Link/Link.spec.js
+++ b/src/client/rsg-components/Link/Link.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import { LinkRenderer } from './LinkRenderer';
 
 const href = '/foo';
diff --git a/src/client/rsg-components/Link/LinkRenderer.js b/src/client/rsg-components/Link/LinkRenderer.js
deleted file mode 100644
index 2baaf846..00000000
--- a/src/client/rsg-components/Link/LinkRenderer.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import cx from 'clsx';
-import Styled from 'rsg-components/Styled';
-
-const styles = ({ color }) => ({
-	link: {
-		'&, &:link, &:visited': {
-			fontSize: 'inherit',
-			color: color.link,
-			textDecoration: 'none',
-		},
-		'&:hover, &:active': {
-			isolate: false,
-			color: color.linkHover,
-			cursor: 'pointer',
-		},
-	},
-});
-
-export function LinkRenderer({ classes, children, ...props }) {
-	return (
-		<a {...props} className={cx(classes.link, props.className)}>
-			{children}
-		</a>
-	);
-}
-
-LinkRenderer.propTypes = {
-	children: PropTypes.node,
-	className: PropTypes.string,
-	classes: PropTypes.object.isRequired,
-};
-
-export default Styled(styles)(LinkRenderer);
diff --git a/src/client/rsg-components/Link/LinkRenderer.tsx b/src/client/rsg-components/Link/LinkRenderer.tsx
new file mode 100644
index 00000000..8cf49d7a
--- /dev/null
+++ b/src/client/rsg-components/Link/LinkRenderer.tsx
@@ -0,0 +1,47 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import cx from 'clsx';
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
+
+const styles = ({ color }: Theme) => ({
+	link: {
+		'&, &:link, &:visited': {
+			fontSize: 'inherit',
+			color: color.link,
+			textDecoration: 'none',
+		},
+		'&:hover, &:active': {
+			isolate: false,
+			color: color.linkHover,
+			cursor: 'pointer',
+		},
+	},
+});
+
+interface LinkProps extends JssInjectedProps {
+	children: React.ReactNode;
+	className?: string;
+	href?: string;
+	target?: string;
+}
+
+export const LinkRenderer: React.FunctionComponent<LinkProps> = ({
+	classes,
+	children,
+	...props
+}) => {
+	return (
+		<a {...props} className={cx(classes.link, props.className)}>
+			{children}
+		</a>
+	);
+};
+
+LinkRenderer.propTypes = {
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
+	children: PropTypes.node,
+	className: PropTypes.string,
+	href: PropTypes.string,
+};
+
+export default Styled<LinkProps>(styles)(LinkRenderer);
diff --git a/src/client/rsg-components/Link/__snapshots__/Link.spec.js.snap b/src/client/rsg-components/Link/__snapshots__/Link.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Link/__snapshots__/Link.spec.js.snap
rename to src/client/rsg-components/Link/__snapshots__/Link.spec.tsx.snap
diff --git a/src/client/rsg-components/Link/index.js b/src/client/rsg-components/Link/index.ts
similarity index 100%
rename from src/client/rsg-components/Link/index.js
rename to src/client/rsg-components/Link/index.ts
diff --git a/src/client/rsg-components/Markdown/Blockquote/Blockquote.spec.js b/src/client/rsg-components/Markdown/Blockquote/Blockquote.spec.tsx
similarity index 93%
rename from src/client/rsg-components/Markdown/Blockquote/Blockquote.spec.js
rename to src/client/rsg-components/Markdown/Blockquote/Blockquote.spec.tsx
index 0ff8f75f..86acb1e3 100644
--- a/src/client/rsg-components/Markdown/Blockquote/Blockquote.spec.js
+++ b/src/client/rsg-components/Markdown/Blockquote/Blockquote.spec.tsx
@@ -1,5 +1,5 @@
 import React from 'react';
-
+import { render } from 'enzyme';
 import Blockquote from './index';
 
 describe('Markdown Blockquote', () => {
diff --git a/src/client/rsg-components/Markdown/Blockquote/BlockquoteRenderer.js b/src/client/rsg-components/Markdown/Blockquote/BlockquoteRenderer.js
deleted file mode 100644
index 0a14b068..00000000
--- a/src/client/rsg-components/Markdown/Blockquote/BlockquoteRenderer.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import cx from 'clsx';
-
-import Styled from 'rsg-components/Styled';
-
-const styles = ({ space, color, fontSize, fontFamily }) => ({
-	blockquote: {
-		margin: [[space[2], space[4]]],
-		padding: 0,
-		color: color.base,
-		fontFamily: fontFamily.base,
-		fontSize: fontSize.base,
-		lineHeight: 1.5,
-	},
-});
-
-export function BlockquoteRenderer({ classes, className, children }) {
-	const blockquoteClasses = cx(classes.blockquote, className);
-	return <blockquote className={blockquoteClasses}>{children}</blockquote>;
-}
-BlockquoteRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
-	className: PropTypes.string,
-	children: PropTypes.node.isRequired,
-};
-
-export default Styled(styles)(BlockquoteRenderer);
diff --git a/src/client/rsg-components/Markdown/Blockquote/BlockquoteRenderer.tsx b/src/client/rsg-components/Markdown/Blockquote/BlockquoteRenderer.tsx
new file mode 100644
index 00000000..fa4cec90
--- /dev/null
+++ b/src/client/rsg-components/Markdown/Blockquote/BlockquoteRenderer.tsx
@@ -0,0 +1,37 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import cx from 'clsx';
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
+
+const styles = ({ space, color, fontSize, fontFamily }: Theme) => ({
+	blockquote: {
+		margin: [[space[2], space[4]]],
+		padding: 0,
+		color: color.base,
+		fontFamily: fontFamily.base,
+		fontSize: fontSize.base,
+		lineHeight: 1.5,
+	},
+});
+
+interface BlockquoteProps extends JssInjectedProps {
+	children: React.ReactNode;
+	className?: string;
+}
+
+export const BlockquoteRenderer: React.FunctionComponent<BlockquoteProps> = ({
+	classes,
+	className,
+	children,
+}) => {
+	const blockquoteClasses = cx(classes.blockquote, className);
+	return <blockquote className={blockquoteClasses}>{children}</blockquote>;
+};
+
+BlockquoteRenderer.propTypes = {
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
+	className: PropTypes.string,
+	children: PropTypes.node.isRequired,
+};
+
+export default Styled<BlockquoteProps>(styles)(BlockquoteRenderer);
diff --git a/src/client/rsg-components/Markdown/Blockquote/__snapshots__/Blockquote.spec.js.snap b/src/client/rsg-components/Markdown/Blockquote/__snapshots__/Blockquote.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Markdown/Blockquote/__snapshots__/Blockquote.spec.js.snap
rename to src/client/rsg-components/Markdown/Blockquote/__snapshots__/Blockquote.spec.tsx.snap
diff --git a/src/client/rsg-components/Markdown/Blockquote/index.js b/src/client/rsg-components/Markdown/Blockquote/index.ts
similarity index 100%
rename from src/client/rsg-components/Markdown/Blockquote/index.js
rename to src/client/rsg-components/Markdown/Blockquote/index.ts
diff --git a/src/client/rsg-components/Markdown/Checkbox/Checkbox.spec.js b/src/client/rsg-components/Markdown/Checkbox/Checkbox.spec.tsx
similarity index 87%
rename from src/client/rsg-components/Markdown/Checkbox/Checkbox.spec.js
rename to src/client/rsg-components/Markdown/Checkbox/Checkbox.spec.tsx
index 21340273..400ee0d2 100644
--- a/src/client/rsg-components/Markdown/Checkbox/Checkbox.spec.js
+++ b/src/client/rsg-components/Markdown/Checkbox/Checkbox.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { render } from 'enzyme';
 
 import Checkbox from './index';
 
diff --git a/src/client/rsg-components/Markdown/Checkbox/CheckboxRenderer.js b/src/client/rsg-components/Markdown/Checkbox/CheckboxRenderer.tsx
similarity index 57%
rename from src/client/rsg-components/Markdown/Checkbox/CheckboxRenderer.js
rename to src/client/rsg-components/Markdown/Checkbox/CheckboxRenderer.tsx
index b767b48d..d2fb7d04 100644
--- a/src/client/rsg-components/Markdown/Checkbox/CheckboxRenderer.js
+++ b/src/client/rsg-components/Markdown/Checkbox/CheckboxRenderer.tsx
@@ -1,7 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 
-import Styled from 'rsg-components/Styled';
+import Styled, { JssInjectedProps } from 'rsg-components/Styled';
 
 const styles = () => ({
 	input: {
@@ -11,11 +11,14 @@ const styles = () => ({
 	},
 });
 
-export function CheckboxRenderer({ classes, ...rest }) {
+export const CheckboxRenderer: React.FunctionComponent<JssInjectedProps> = ({
+	classes,
+	...rest
+}) => {
 	return <input {...rest} type="checkbox" className={classes.input} />;
-}
+};
 CheckboxRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 };
 
 export default Styled(styles)(CheckboxRenderer);
diff --git a/src/client/rsg-components/Markdown/Checkbox/__snapshots__/Checkbox.spec.js.snap b/src/client/rsg-components/Markdown/Checkbox/__snapshots__/Checkbox.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Markdown/Checkbox/__snapshots__/Checkbox.spec.js.snap
rename to src/client/rsg-components/Markdown/Checkbox/__snapshots__/Checkbox.spec.tsx.snap
diff --git a/src/client/rsg-components/Markdown/Details/Details.spec.js b/src/client/rsg-components/Markdown/Details/Details.spec.tsx
similarity index 90%
rename from src/client/rsg-components/Markdown/Details/Details.spec.js
rename to src/client/rsg-components/Markdown/Details/Details.spec.tsx
index c2c71352..b064537f 100644
--- a/src/client/rsg-components/Markdown/Details/Details.spec.js
+++ b/src/client/rsg-components/Markdown/Details/Details.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { render } from 'enzyme';
 
 import { Details, DetailsSummary } from './index';
 
diff --git a/src/client/rsg-components/Markdown/Details/DetailsRenderer.js b/src/client/rsg-components/Markdown/Details/DetailsRenderer.js
deleted file mode 100644
index dc9d4240..00000000
--- a/src/client/rsg-components/Markdown/Details/DetailsRenderer.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
-
-const styles = ({ space, color, fontSize, fontFamily }) => ({
-	details: {
-		marginBottom: space[2],
-		fontFamily: fontFamily.base,
-		fontSize: fontSize.base,
-		color: color.base,
-	},
-});
-
-export function DetailsRenderer({ classes, children }) {
-	return <details className={classes.details}>{children}</details>;
-}
-
-DetailsRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
-	children: PropTypes.node.isRequired,
-};
-
-export default Styled(styles)(DetailsRenderer);
diff --git a/src/client/rsg-components/Markdown/Details/DetailsRenderer.tsx b/src/client/rsg-components/Markdown/Details/DetailsRenderer.tsx
new file mode 100644
index 00000000..9f3dc46f
--- /dev/null
+++ b/src/client/rsg-components/Markdown/Details/DetailsRenderer.tsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
+
+const styles = ({ space, color, fontSize, fontFamily }: Theme) => ({
+	details: {
+		marginBottom: space[2],
+		fontFamily: fontFamily.base,
+		fontSize: fontSize.base,
+		color: color.base,
+	},
+});
+
+interface DetailsProps extends JssInjectedProps {
+	children: React.ReactNode;
+}
+
+export const DetailsRenderer: React.FunctionComponent<DetailsProps> = ({ classes, children }) => {
+	return <details className={classes.details}>{children}</details>;
+};
+
+DetailsRenderer.propTypes = {
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
+	children: PropTypes.node.isRequired,
+};
+
+export default Styled<DetailsProps>(styles)(DetailsRenderer);
diff --git a/src/client/rsg-components/Markdown/Details/DetailsSummaryRenderer.js b/src/client/rsg-components/Markdown/Details/DetailsSummaryRenderer.js
deleted file mode 100644
index 417ba027..00000000
--- a/src/client/rsg-components/Markdown/Details/DetailsSummaryRenderer.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
-
-const styles = ({ space, color, fontSize, fontFamily }) => ({
-	summary: {
-		marginBottom: space[1],
-		fontFamily: fontFamily.base,
-		fontSize: fontSize.base,
-		color: color.base,
-		cursor: 'pointer',
-		'&:focus': {
-			isolate: false,
-			outline: [[1, 'dotted', color.linkHover]],
-			outlineOffset: 2,
-		},
-	},
-});
-
-export function DetailsSummaryRenderer({ classes, children }) {
-	return <summary className={classes.summary}>{children}</summary>;
-}
-
-DetailsSummaryRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
-	children: PropTypes.node.isRequired,
-};
-
-export default Styled(styles)(DetailsSummaryRenderer);
diff --git a/src/client/rsg-components/Markdown/Details/DetailsSummaryRenderer.tsx b/src/client/rsg-components/Markdown/Details/DetailsSummaryRenderer.tsx
new file mode 100644
index 00000000..bec68000
--- /dev/null
+++ b/src/client/rsg-components/Markdown/Details/DetailsSummaryRenderer.tsx
@@ -0,0 +1,36 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
+
+const styles = ({ space, color, fontSize, fontFamily }: Theme) => ({
+	summary: {
+		marginBottom: space[1],
+		fontFamily: fontFamily.base,
+		fontSize: fontSize.base,
+		color: color.base,
+		cursor: 'pointer',
+		'&:focus': {
+			isolate: false,
+			outline: [[1, 'dotted', color.linkHover]],
+			outlineOffset: 2,
+		},
+	},
+});
+
+interface DetailsSummaryProps extends JssInjectedProps {
+	children: React.ReactNode;
+}
+
+export const DetailsSummaryRenderer: React.FunctionComponent<DetailsSummaryProps> = ({
+	classes,
+	children,
+}) => {
+	return <summary className={classes.summary}>{children}</summary>;
+};
+
+DetailsSummaryRenderer.propTypes = {
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
+	children: PropTypes.node.isRequired,
+};
+
+export default Styled<DetailsSummaryProps>(styles)(DetailsSummaryRenderer);
diff --git a/src/client/rsg-components/Markdown/Details/__snapshots__/Details.spec.js.snap b/src/client/rsg-components/Markdown/Details/__snapshots__/Details.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Markdown/Details/__snapshots__/Details.spec.js.snap
rename to src/client/rsg-components/Markdown/Details/__snapshots__/Details.spec.tsx.snap
diff --git a/src/client/rsg-components/Markdown/Details/index.js b/src/client/rsg-components/Markdown/Details/index.ts
similarity index 100%
rename from src/client/rsg-components/Markdown/Details/index.js
rename to src/client/rsg-components/Markdown/Details/index.ts
diff --git a/src/client/rsg-components/Markdown/Hr/Hr.spec.js b/src/client/rsg-components/Markdown/Hr/Hr.spec.tsx
similarity index 86%
rename from src/client/rsg-components/Markdown/Hr/Hr.spec.js
rename to src/client/rsg-components/Markdown/Hr/Hr.spec.tsx
index c387e777..58fd604f 100644
--- a/src/client/rsg-components/Markdown/Hr/Hr.spec.js
+++ b/src/client/rsg-components/Markdown/Hr/Hr.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { render } from 'enzyme';
 
 import Hr from './index';
 
diff --git a/src/client/rsg-components/Markdown/Hr/HrRenderer.js b/src/client/rsg-components/Markdown/Hr/HrRenderer.js
deleted file mode 100644
index 56351967..00000000
--- a/src/client/rsg-components/Markdown/Hr/HrRenderer.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import Styled from 'rsg-components/Styled';
-
-const styles = ({ space, color }) => ({
-	hr: {
-		borderBottom: [[1, color.border, 'solid']],
-		marginTop: 0,
-		marginBottom: space[2],
-	},
-});
-
-export function HrRenderer({ classes }) {
-	return <hr className={classes.hr} />;
-}
-HrRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
-};
-
-export default Styled(styles)(HrRenderer);
diff --git a/src/client/rsg-components/Markdown/Hr/HrRenderer.tsx b/src/client/rsg-components/Markdown/Hr/HrRenderer.tsx
new file mode 100644
index 00000000..722e9395
--- /dev/null
+++ b/src/client/rsg-components/Markdown/Hr/HrRenderer.tsx
@@ -0,0 +1,21 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
+
+const styles = ({ space, color }: Theme) => ({
+	hr: {
+		borderBottom: [[1, color.border, 'solid']],
+		marginTop: 0,
+		marginBottom: space[2],
+	},
+});
+
+export const HrRenderer: React.FunctionComponent<JssInjectedProps> = ({ classes }) => {
+	return <hr className={classes.hr} />;
+};
+HrRenderer.propTypes = {
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
+};
+
+export default Styled<JssInjectedProps>(styles)(HrRenderer);
diff --git a/src/client/rsg-components/Markdown/Hr/__snapshots__/Hr.spec.js.snap b/src/client/rsg-components/Markdown/Hr/__snapshots__/Hr.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Markdown/Hr/__snapshots__/Hr.spec.js.snap
rename to src/client/rsg-components/Markdown/Hr/__snapshots__/Hr.spec.tsx.snap
diff --git a/src/client/rsg-components/Markdown/Hr/index.js b/src/client/rsg-components/Markdown/Hr/index.ts
similarity index 100%
rename from src/client/rsg-components/Markdown/Hr/index.js
rename to src/client/rsg-components/Markdown/Hr/index.ts
diff --git a/src/client/rsg-components/Markdown/List/List.spec.js b/src/client/rsg-components/Markdown/List/List.spec.tsx
similarity index 93%
rename from src/client/rsg-components/Markdown/List/List.spec.js
rename to src/client/rsg-components/Markdown/List/List.spec.tsx
index 00d1e29b..52da543a 100644
--- a/src/client/rsg-components/Markdown/List/List.spec.js
+++ b/src/client/rsg-components/Markdown/List/List.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { render } from 'enzyme';
 
 import List from './index';
 
diff --git a/src/client/rsg-components/Markdown/List/ListRenderer.js b/src/client/rsg-components/Markdown/List/ListRenderer.tsx
similarity index 50%
rename from src/client/rsg-components/Markdown/List/ListRenderer.js
rename to src/client/rsg-components/Markdown/List/ListRenderer.tsx
index 6a1028c9..b329f658 100644
--- a/src/client/rsg-components/Markdown/List/ListRenderer.js
+++ b/src/client/rsg-components/Markdown/List/ListRenderer.tsx
@@ -1,9 +1,9 @@
-import React, { Children, cloneElement } from 'react';
+import React, { cloneElement, Children } from 'react';
 import PropTypes from 'prop-types';
 import cx from 'clsx';
-import Styled from 'rsg-components/Styled';
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
 
-const styles = ({ space, color, fontFamily }) => ({
+const styles = ({ space, color, fontFamily }: Theme) => ({
 	list: {
 		marginTop: 0,
 		marginBottom: space[2],
@@ -22,19 +22,30 @@ const styles = ({ space, color, fontFamily }) => ({
 	},
 });
 
-export function ListRenderer({ classes, ordered, children }) {
+interface ListProps extends JssInjectedProps {
+	ordered?: boolean;
+	children: React.ReactNode;
+}
+
+export const ListRenderer: React.FunctionComponent<ListProps> = ({
+	classes,
+	ordered,
+	children,
+}) => {
 	const Tag = ordered ? 'ol' : 'ul';
 
 	const classNames = cx(classes.list, ordered && classes.ordered);
 
 	return (
 		<Tag className={classNames}>
-			{Children.map(children, li => cloneElement(li, { className: classes.li }))}
+			{Children.map(children, li =>
+				React.isValidElement(li) ? cloneElement(li, { className: classes.li }) : li
+			)}
 		</Tag>
 	);
-}
+};
 ListRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	ordered: PropTypes.bool,
 	children: PropTypes.node.isRequired,
 };
@@ -42,4 +53,4 @@ ListRenderer.defaultProps = {
 	ordered: false,
 };
 
-export default Styled(styles)(ListRenderer);
+export default Styled<ListProps>(styles)(ListRenderer);
diff --git a/src/client/rsg-components/Markdown/List/__snapshots__/List.spec.js.snap b/src/client/rsg-components/Markdown/List/__snapshots__/List.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Markdown/List/__snapshots__/List.spec.js.snap
rename to src/client/rsg-components/Markdown/List/__snapshots__/List.spec.tsx.snap
diff --git a/src/client/rsg-components/Markdown/List/index.js b/src/client/rsg-components/Markdown/List/index.ts
similarity index 100%
rename from src/client/rsg-components/Markdown/List/index.js
rename to src/client/rsg-components/Markdown/List/index.ts
diff --git a/src/client/rsg-components/Markdown/Markdown.spec.js b/src/client/rsg-components/Markdown/Markdown.spec.tsx
similarity index 88%
rename from src/client/rsg-components/Markdown/Markdown.spec.js
rename to src/client/rsg-components/Markdown/Markdown.spec.tsx
index 52718310..635f0def 100644
--- a/src/client/rsg-components/Markdown/Markdown.spec.js
+++ b/src/client/rsg-components/Markdown/Markdown.spec.tsx
@@ -1,9 +1,10 @@
 import React from 'react';
 import { html } from 'cheerio';
+import { render, mount } from 'enzyme';
 import Markdown from './Markdown';
 
 describe('Markdown', () => {
-	const expectSnapshotToMatch = markdown => {
+	const expectSnapshotToMatch = (markdown: string) => {
 		const actual = render(<Markdown text={markdown} />);
 
 		expect(html(actual)).toMatchSnapshot();
@@ -129,8 +130,9 @@ and this is _emphasized_
 <!-- This is a single line comment -->
 `;
 		const actual = mount(<Markdown text={markdown} />);
+		const pChildren = actual.find('p').props().children as string[];
 
-		expect(actual.find('p').props().children[0]).not.toContain('This is a single line comment');
+		expect(pChildren[0]).not.toContain('This is a single line comment');
 	});
 
 	it('should ignore multiline comments', () => {
@@ -142,8 +144,9 @@ comment
 -->
 `;
 		const actual = mount(<Markdown text={markdown} />);
+		const pChildren = actual.find('p').props().children as string[];
 
-		expect(actual.find('p').props().children[0]).not.toContain(
+		expect(pChildren[0]).not.toContain(
 			`This is a 
 multiline
 comment`
@@ -152,7 +155,7 @@ comment`
 });
 
 describe('Markdown inline', () => {
-	const expectSnapshotToMatch = markdown => {
+	const expectSnapshotToMatch = (markdown: string) => {
 		const actual = render(<Markdown text={markdown} inline />);
 
 		expect(html(actual)).toMatchSnapshot();
diff --git a/src/client/rsg-components/Markdown/Markdown.js b/src/client/rsg-components/Markdown/Markdown.tsx
similarity index 63%
rename from src/client/rsg-components/Markdown/Markdown.js
rename to src/client/rsg-components/Markdown/Markdown.tsx
index 0dbefe78..593ec144 100644
--- a/src/client/rsg-components/Markdown/Markdown.js
+++ b/src/client/rsg-components/Markdown/Markdown.tsx
@@ -8,14 +8,14 @@ import Para from 'rsg-components/Para';
 import MarkdownHeading from 'rsg-components/Markdown/MarkdownHeading';
 import List from 'rsg-components/Markdown/List';
 import Blockquote from 'rsg-components/Markdown/Blockquote';
-import PreBase from 'rsg-components/Markdown/Pre';
+import PreBase, { PreProps } from 'rsg-components/Markdown/Pre';
 import Code from 'rsg-components/Code';
 import Checkbox from 'rsg-components/Markdown/Checkbox';
 import Hr from 'rsg-components/Markdown/Hr';
 import { Details, DetailsSummary } from 'rsg-components/Markdown/Details';
 import { Table, TableHead, TableBody, TableRow, TableCell } from 'rsg-components/Markdown/Table';
 
-const Pre = props => {
+const Pre = (props: PreProps) => {
 	if (isValidElement(props.children)) {
 		// Avoid rendering <Code> inside <Pre>
 		return <PreBase {...props.children.props} />;
@@ -28,112 +28,112 @@ Pre.propTypes = {
 
 export const baseOverrides = {
 	a: {
-		component: Link,
+		component: Link as React.SFC,
 	},
 	h1: {
-		component: MarkdownHeading,
+		component: MarkdownHeading as React.SFC,
 		props: {
 			level: 1,
 		},
 	},
 	h2: {
-		component: MarkdownHeading,
+		component: MarkdownHeading as React.SFC,
 		props: {
 			level: 2,
 		},
 	},
 	h3: {
-		component: MarkdownHeading,
+		component: MarkdownHeading as React.SFC,
 		props: {
 			level: 3,
 		},
 	},
 	h4: {
-		component: MarkdownHeading,
+		component: MarkdownHeading as React.SFC,
 		props: {
 			level: 4,
 		},
 	},
 	h5: {
-		component: MarkdownHeading,
+		component: MarkdownHeading as React.SFC,
 		props: {
 			level: 5,
 		},
 	},
 	h6: {
-		component: MarkdownHeading,
+		component: MarkdownHeading as React.SFC,
 		props: {
 			level: 6,
 		},
 	},
 	p: {
-		component: Para,
+		component: Para as React.SFC,
 		props: {
 			semantic: 'p',
 		},
 	},
 	em: {
-		component: Text,
+		component: Text as React.SFC,
 		props: {
 			semantic: 'em',
 		},
 	},
 	strong: {
-		component: Text,
+		component: Text as React.SFC,
 		props: {
 			semantic: 'strong',
 		},
 	},
 	ul: {
-		component: List,
+		component: List as React.SFC,
 	},
 	ol: {
-		component: List,
+		component: List as React.SFC,
 		props: {
 			ordered: true,
 		},
 	},
 	blockquote: {
-		component: Blockquote,
+		component: Blockquote as React.SFC,
 	},
 	code: {
-		component: Code,
+		component: Code as React.SFC,
 	},
 	pre: {
-		component: Pre,
+		component: Pre as React.SFC,
 	},
 	input: {
-		component: Checkbox,
+		component: Checkbox as React.SFC,
 	},
 	hr: {
-		component: Hr,
+		component: Hr as React.SFC,
 	},
 	table: {
-		component: Table,
+		component: Table as React.SFC,
 	},
 	thead: {
-		component: TableHead,
+		component: TableHead as React.SFC,
 	},
 	th: {
-		component: TableCell,
+		component: TableCell as React.SFC,
 		props: {
 			header: true,
 		},
 	},
 	tbody: {
-		component: TableBody,
+		component: TableBody as React.SFC,
 	},
 	tr: {
-		component: TableRow,
+		component: TableRow as React.SFC,
 	},
 	td: {
-		component: TableCell,
+		component: TableCell as React.SFC,
 	},
 	details: {
-		component: Details,
+		component: Details as React.SFC,
 	},
 	summary: {
-		component: DetailsSummary,
+		component: DetailsSummary as React.SFC,
 	},
 };
 
@@ -144,10 +144,15 @@ export const inlineOverrides = {
 	},
 };
 
-function Markdown({ text, inline }) {
+interface MarkdownProps {
+	text: string;
+	inline?: boolean;
+}
+
+export const Markdown: React.FunctionComponent<MarkdownProps> = ({ text, inline }) => {
 	const overrides = inline ? inlineOverrides : baseOverrides;
 	return compiler(stripHtmlComments(text), { overrides, forceBlock: true });
-}
+};
 
 Markdown.propTypes = {
 	text: PropTypes.string.isRequired,
diff --git a/src/client/rsg-components/Markdown/MarkdownHeading/MarkdownHeading.spec.js b/src/client/rsg-components/Markdown/MarkdownHeading/MarkdownHeading.spec.tsx
similarity index 92%
rename from src/client/rsg-components/Markdown/MarkdownHeading/MarkdownHeading.spec.js
rename to src/client/rsg-components/Markdown/MarkdownHeading/MarkdownHeading.spec.tsx
index 7d9b8cf3..3aa2543c 100644
--- a/src/client/rsg-components/Markdown/MarkdownHeading/MarkdownHeading.spec.js
+++ b/src/client/rsg-components/Markdown/MarkdownHeading/MarkdownHeading.spec.tsx
@@ -1,5 +1,6 @@
 import React from 'react';
 import { html } from 'cheerio';
+import { render } from 'enzyme';
 import MarkdownHeading from './index';
 
 describe('Markdown Heading', () => {
diff --git a/src/client/rsg-components/Markdown/MarkdownHeading/MarkdownHeadingRenderer.js b/src/client/rsg-components/Markdown/MarkdownHeading/MarkdownHeadingRenderer.js
deleted file mode 100644
index 5edf963c..00000000
--- a/src/client/rsg-components/Markdown/MarkdownHeading/MarkdownHeadingRenderer.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
-import Heading from 'rsg-components/Heading';
-
-const styles = ({ space }) => ({
-	spacing: {
-		marginBottom: space[2],
-	},
-});
-
-function MarkdownHeadingRenderer({ classes, level, children, id }) {
-	return (
-		<div className={classes.spacing}>
-			<Heading level={level} id={id}>
-				{children}
-			</Heading>
-		</div>
-	);
-}
-
-MarkdownHeadingRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
-	level: PropTypes.oneOf([1, 2, 3, 4, 5, 6]).isRequired,
-	children: PropTypes.node,
-	id: PropTypes.string,
-};
-
-export default Styled(styles)(MarkdownHeadingRenderer);
diff --git a/src/client/rsg-components/Markdown/MarkdownHeading/MarkdownHeadingRenderer.tsx b/src/client/rsg-components/Markdown/MarkdownHeading/MarkdownHeadingRenderer.tsx
new file mode 100644
index 00000000..2206161a
--- /dev/null
+++ b/src/client/rsg-components/Markdown/MarkdownHeading/MarkdownHeadingRenderer.tsx
@@ -0,0 +1,40 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
+import Heading from 'rsg-components/Heading';
+
+const styles = ({ space }: Theme) => ({
+	spacing: {
+		marginBottom: space[2],
+	},
+});
+
+interface MarkdownHeadingProps extends JssInjectedProps {
+	children: React.ReactNode;
+	level: number;
+	id?: string;
+}
+
+const MarkdownHeadingRenderer: React.FunctionComponent<MarkdownHeadingProps> = ({
+	classes,
+	level,
+	children,
+	id,
+}) => {
+	return (
+		<div className={classes.spacing}>
+			<Heading level={level} id={id}>
+				{children}
+			</Heading>
+		</div>
+	);
+};
+
+MarkdownHeadingRenderer.propTypes = {
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
+	level: PropTypes.oneOf([1, 2, 3, 4, 5, 6]).isRequired,
+	children: PropTypes.node,
+	id: PropTypes.string,
+};
+
+export default Styled<MarkdownHeadingProps>(styles)(MarkdownHeadingRenderer);
diff --git a/src/client/rsg-components/Markdown/MarkdownHeading/__snapshots__/MarkdownHeading.spec.js.snap b/src/client/rsg-components/Markdown/MarkdownHeading/__snapshots__/MarkdownHeading.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Markdown/MarkdownHeading/__snapshots__/MarkdownHeading.spec.js.snap
rename to src/client/rsg-components/Markdown/MarkdownHeading/__snapshots__/MarkdownHeading.spec.tsx.snap
diff --git a/src/client/rsg-components/Markdown/MarkdownHeading/index.js b/src/client/rsg-components/Markdown/MarkdownHeading/index.ts
similarity index 100%
rename from src/client/rsg-components/Markdown/MarkdownHeading/index.js
rename to src/client/rsg-components/Markdown/MarkdownHeading/index.ts
diff --git a/src/client/rsg-components/Markdown/Pre/Pre.spec.js b/src/client/rsg-components/Markdown/Pre/Pre.spec.tsx
similarity index 92%
rename from src/client/rsg-components/Markdown/Pre/Pre.spec.js
rename to src/client/rsg-components/Markdown/Pre/Pre.spec.tsx
index f1ba3fa9..d715e068 100644
--- a/src/client/rsg-components/Markdown/Pre/Pre.spec.js
+++ b/src/client/rsg-components/Markdown/Pre/Pre.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { render } from 'enzyme';
 
 import Pre from './index';
 
diff --git a/src/client/rsg-components/Markdown/Pre/PreRenderer.js b/src/client/rsg-components/Markdown/Pre/PreRenderer.tsx
similarity index 63%
rename from src/client/rsg-components/Markdown/Pre/PreRenderer.js
rename to src/client/rsg-components/Markdown/Pre/PreRenderer.tsx
index 6bcc5687..549b18d2 100644
--- a/src/client/rsg-components/Markdown/Pre/PreRenderer.js
+++ b/src/client/rsg-components/Markdown/Pre/PreRenderer.tsx
@@ -1,10 +1,10 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import cx from 'clsx';
-import Styled from 'rsg-components/Styled';
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
 import prismTheme from '../../../styles/prismTheme';
 
-const styles = ({ space, color, fontSize, fontFamily, borderRadius }) => ({
+const styles = ({ space, color, fontSize, fontFamily, borderRadius }: Theme) => ({
 	pre: {
 		fontFamily: fontFamily.monospace,
 		fontSize: fontSize.small,
@@ -24,20 +24,31 @@ const styles = ({ space, color, fontSize, fontFamily, borderRadius }) => ({
 	},
 });
 
-export function PreRenderer({ classes, className, children }) {
+export interface PreProps {
+	className?: string;
+	children: React.ReactNode;
+}
+
+type PrePropsWithClasses = JssInjectedProps & PreProps;
+
+export const PreRenderer: React.FunctionComponent<PrePropsWithClasses> = ({
+	classes,
+	className,
+	children,
+}) => {
 	const classNames = cx(className, classes.pre);
 
 	const isHighlighted = className && className.indexOf('lang-') !== -1;
-	if (isHighlighted) {
-		return <pre className={classNames} dangerouslySetInnerHTML={{ __html: children }} />;
+	if (isHighlighted && children) {
+		return <pre className={classNames} dangerouslySetInnerHTML={{ __html: children.toString() }} />;
 	}
 	return <pre className={classNames}>{children}</pre>;
-}
+};
 
 PreRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	className: PropTypes.string,
 	children: PropTypes.node.isRequired,
 };
 
-export default Styled(styles)(PreRenderer);
+export default Styled<PrePropsWithClasses>(styles)(PreRenderer);
diff --git a/src/client/rsg-components/Markdown/Pre/__snapshots__/Pre.spec.js.snap b/src/client/rsg-components/Markdown/Pre/__snapshots__/Pre.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Markdown/Pre/__snapshots__/Pre.spec.js.snap
rename to src/client/rsg-components/Markdown/Pre/__snapshots__/Pre.spec.tsx.snap
diff --git a/src/client/rsg-components/Markdown/Pre/index.js b/src/client/rsg-components/Markdown/Pre/index.js
deleted file mode 100644
index 77d77969..00000000
--- a/src/client/rsg-components/Markdown/Pre/index.js
+++ /dev/null
@@ -1,1 +0,0 @@
-export { default } from 'rsg-components/Markdown/Pre/PreRenderer';
diff --git a/src/client/rsg-components/Markdown/Pre/index.ts b/src/client/rsg-components/Markdown/Pre/index.ts
new file mode 100644
index 00000000..d94035f7
--- /dev/null
+++ b/src/client/rsg-components/Markdown/Pre/index.ts
@@ -0,0 +1,1 @@
+export { default, PreProps } from 'rsg-components/Markdown/Pre/PreRenderer';
diff --git a/src/client/rsg-components/Markdown/Table/Table.spec.js b/src/client/rsg-components/Markdown/Table/Table.spec.tsx
similarity index 94%
rename from src/client/rsg-components/Markdown/Table/Table.spec.js
rename to src/client/rsg-components/Markdown/Table/Table.spec.tsx
index 9c8f2add..66991415 100644
--- a/src/client/rsg-components/Markdown/Table/Table.spec.js
+++ b/src/client/rsg-components/Markdown/Table/Table.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { render } from 'enzyme';
 
 import { Table, TableHead, TableBody, TableRow, TableCell } from './index';
 
diff --git a/src/client/rsg-components/Markdown/Table/TableBodyRenderer.js b/src/client/rsg-components/Markdown/Table/TableBodyRenderer.tsx
similarity index 71%
rename from src/client/rsg-components/Markdown/Table/TableBodyRenderer.js
rename to src/client/rsg-components/Markdown/Table/TableBodyRenderer.tsx
index 17723feb..dbab3224 100644
--- a/src/client/rsg-components/Markdown/Table/TableBodyRenderer.js
+++ b/src/client/rsg-components/Markdown/Table/TableBodyRenderer.tsx
@@ -1,9 +1,9 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 
-export function TableBodyRenderer({ children }) {
+export const TableBodyRenderer: React.FunctionComponent = ({ children }) => {
 	return <tbody>{children}</tbody>;
-}
+};
 TableBodyRenderer.propTypes = {
 	children: PropTypes.node.isRequired,
 };
diff --git a/src/client/rsg-components/Markdown/Table/TableCellRenderer.js b/src/client/rsg-components/Markdown/Table/TableCellRenderer.tsx
similarity index 52%
rename from src/client/rsg-components/Markdown/Table/TableCellRenderer.js
rename to src/client/rsg-components/Markdown/Table/TableCellRenderer.tsx
index 9d0f8262..e2512c07 100644
--- a/src/client/rsg-components/Markdown/Table/TableCellRenderer.js
+++ b/src/client/rsg-components/Markdown/Table/TableCellRenderer.tsx
@@ -1,9 +1,9 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 
-import Styled from 'rsg-components/Styled';
+import Styled, { Theme, JssInjectedProps } from 'rsg-components/Styled';
 
-const styles = ({ space, color, fontSize, fontFamily }) => ({
+const styles = ({ space, color, fontSize, fontFamily }: Theme) => ({
 	td: {
 		padding: [[space[0], space[2], space[0], 0]],
 		fontFamily: fontFamily.base,
@@ -17,15 +17,25 @@ const styles = ({ space, color, fontSize, fontFamily }) => ({
 	},
 });
 
-export function TableCellRenderer({ classes, header, children }) {
+interface TableCellProps extends JssInjectedProps {
+	children: React.ReactNode;
+	header?: boolean;
+}
+
+export const TableCellRenderer: React.FunctionComponent<TableCellProps> = ({
+	classes,
+	header,
+	children,
+}) => {
 	if (header) {
 		return <th className={classes.th}>{children}</th>;
 	}
 
 	return <td className={classes.td}>{children}</td>;
-}
+};
+
 TableCellRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	header: PropTypes.bool,
 	children: PropTypes.node.isRequired,
 };
@@ -33,4 +43,4 @@ TableCellRenderer.defaultProps = {
 	header: false,
 };
 
-export default Styled(styles)(TableCellRenderer);
+export default Styled<TableCellProps>(styles)(TableCellRenderer);
diff --git a/src/client/rsg-components/Markdown/Table/TableHeadRenderer.js b/src/client/rsg-components/Markdown/Table/TableHeadRenderer.js
deleted file mode 100644
index 975b66ba..00000000
--- a/src/client/rsg-components/Markdown/Table/TableHeadRenderer.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import Styled from 'rsg-components/Styled';
-
-const styles = ({ color }) => ({
-	thead: {
-		borderBottom: [[1, color.border, 'solid']],
-	},
-});
-
-export function TableHeadRenderer({ classes, children }) {
-	return <thead className={classes.thead}>{children}</thead>;
-}
-TableHeadRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
-	children: PropTypes.node.isRequired,
-};
-
-export default Styled(styles)(TableHeadRenderer);
diff --git a/src/client/rsg-components/Markdown/Table/TableHeadRenderer.tsx b/src/client/rsg-components/Markdown/Table/TableHeadRenderer.tsx
new file mode 100644
index 00000000..accfabd4
--- /dev/null
+++ b/src/client/rsg-components/Markdown/Table/TableHeadRenderer.tsx
@@ -0,0 +1,28 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import Styled, { Theme, JssInjectedProps } from 'rsg-components/Styled';
+
+const styles = ({ color }: Theme) => ({
+	thead: {
+		borderBottom: [[1, color.border, 'solid']],
+	},
+});
+
+interface TableHeadProps extends JssInjectedProps {
+	children: React.ReactNode;
+}
+
+export const TableHeadRenderer: React.FunctionComponent<TableHeadProps> = ({
+	classes,
+	children,
+}) => {
+	return <thead className={classes.thead}>{children}</thead>;
+};
+
+TableHeadRenderer.propTypes = {
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
+	children: PropTypes.node.isRequired,
+};
+
+export default Styled<TableHeadProps>(styles)(TableHeadRenderer);
diff --git a/src/client/rsg-components/Markdown/Table/TableRenderer.js b/src/client/rsg-components/Markdown/Table/TableRenderer.js
deleted file mode 100644
index b3d90283..00000000
--- a/src/client/rsg-components/Markdown/Table/TableRenderer.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import Styled from 'rsg-components/Styled';
-
-const styles = ({ space }) => ({
-	table: {
-		marginTop: 0,
-		marginBottom: space[2],
-		borderCollapse: 'collapse',
-	},
-});
-
-export function TableRenderer({ classes, children }) {
-	return <table className={classes.table}>{children}</table>;
-}
-TableRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
-	children: PropTypes.node.isRequired,
-};
-
-export default Styled(styles)(TableRenderer);
diff --git a/src/client/rsg-components/Markdown/Table/TableRenderer.tsx b/src/client/rsg-components/Markdown/Table/TableRenderer.tsx
new file mode 100644
index 00000000..56fb2aec
--- /dev/null
+++ b/src/client/rsg-components/Markdown/Table/TableRenderer.tsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import Styled, { Theme, JssInjectedProps } from 'rsg-components/Styled';
+
+const styles = ({ space }: Theme) => ({
+	table: {
+		marginTop: 0,
+		marginBottom: space[2],
+		borderCollapse: 'collapse',
+	},
+});
+
+interface TableProps extends JssInjectedProps {
+	children: React.ReactNode;
+}
+
+export const TableRenderer: React.FunctionComponent<TableProps> = ({ classes, children }) => {
+	return <table className={classes.table}>{children}</table>;
+};
+
+TableRenderer.propTypes = {
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
+	children: PropTypes.node.isRequired,
+};
+
+export default Styled<TableProps>(styles)(TableRenderer);
diff --git a/src/client/rsg-components/Markdown/Table/TableRowRenderer.js b/src/client/rsg-components/Markdown/Table/TableRowRenderer.tsx
similarity index 71%
rename from src/client/rsg-components/Markdown/Table/TableRowRenderer.js
rename to src/client/rsg-components/Markdown/Table/TableRowRenderer.tsx
index 6913aeb5..26a7a309 100644
--- a/src/client/rsg-components/Markdown/Table/TableRowRenderer.js
+++ b/src/client/rsg-components/Markdown/Table/TableRowRenderer.tsx
@@ -1,9 +1,9 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 
-export function TableRowRenderer({ children }) {
+export const TableRowRenderer: React.FunctionComponent = ({ children }) => {
 	return <tr>{children}</tr>;
-}
+};
 TableRowRenderer.propTypes = {
 	children: PropTypes.node.isRequired,
 };
diff --git a/src/client/rsg-components/Markdown/Table/__snapshots__/Table.spec.js.snap b/src/client/rsg-components/Markdown/Table/__snapshots__/Table.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Markdown/Table/__snapshots__/Table.spec.js.snap
rename to src/client/rsg-components/Markdown/Table/__snapshots__/Table.spec.tsx.snap
diff --git a/src/client/rsg-components/Markdown/Table/index.js b/src/client/rsg-components/Markdown/Table/index.ts
similarity index 100%
rename from src/client/rsg-components/Markdown/Table/index.js
rename to src/client/rsg-components/Markdown/Table/index.ts
diff --git a/src/client/rsg-components/Markdown/__snapshots__/Markdown.spec.js.snap b/src/client/rsg-components/Markdown/__snapshots__/Markdown.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Markdown/__snapshots__/Markdown.spec.js.snap
rename to src/client/rsg-components/Markdown/__snapshots__/Markdown.spec.tsx.snap
diff --git a/src/client/rsg-components/Markdown/index.js b/src/client/rsg-components/Markdown/index.ts
similarity index 100%
rename from src/client/rsg-components/Markdown/index.js
rename to src/client/rsg-components/Markdown/index.ts
diff --git a/src/client/rsg-components/Message/Message.spec.js b/src/client/rsg-components/Message/Message.spec.tsx
similarity index 93%
rename from src/client/rsg-components/Message/Message.spec.js
rename to src/client/rsg-components/Message/Message.spec.tsx
index 825593f6..a19006ca 100644
--- a/src/client/rsg-components/Message/Message.spec.js
+++ b/src/client/rsg-components/Message/Message.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import { MessageRenderer } from './MessageRenderer';
 
 it('renderer should render message', () => {
diff --git a/src/client/rsg-components/Message/MessageRenderer.js b/src/client/rsg-components/Message/MessageRenderer.js
deleted file mode 100644
index 74894a07..00000000
--- a/src/client/rsg-components/Message/MessageRenderer.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Markdown from 'rsg-components/Markdown';
-import Styled from 'rsg-components/Styled';
-
-const styles = ({ space }) => ({
-	root: {
-		marginBottom: space[4],
-	},
-});
-
-export function MessageRenderer({ classes, children }) {
-	return (
-		<div className={classes.root}>
-			<Markdown text={Array.isArray(children) ? children.join('\n') : children} />
-		</div>
-	);
-}
-
-MessageRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
-	children: PropTypes.node.isRequired,
-};
-
-export default Styled(styles)(MessageRenderer);
diff --git a/src/client/rsg-components/Message/MessageRenderer.tsx b/src/client/rsg-components/Message/MessageRenderer.tsx
new file mode 100644
index 00000000..1ddc31ab
--- /dev/null
+++ b/src/client/rsg-components/Message/MessageRenderer.tsx
@@ -0,0 +1,37 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Markdown from 'rsg-components/Markdown';
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
+
+const styles = ({ space }: Theme) => ({
+	root: {
+		marginBottom: space[4],
+	},
+});
+
+interface MessageProps extends JssInjectedProps {
+	children: React.ReactNode;
+}
+
+export const MessageRenderer: React.FunctionComponent<MessageProps> = ({ classes, children }) => {
+	return (
+		<div className={classes.root}>
+			<Markdown
+				text={
+					Array.isArray(children)
+						? children.join('\n')
+						: typeof children === 'string'
+						? children
+						: ''
+				}
+			/>
+		</div>
+	);
+};
+
+MessageRenderer.propTypes = {
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
+	children: PropTypes.node.isRequired,
+};
+
+export default Styled<MessageProps>(styles)(MessageRenderer);
diff --git a/src/client/rsg-components/Message/__snapshots__/Message.spec.js.snap b/src/client/rsg-components/Message/__snapshots__/Message.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Message/__snapshots__/Message.spec.js.snap
rename to src/client/rsg-components/Message/__snapshots__/Message.spec.tsx.snap
diff --git a/src/client/rsg-components/Message/index.js b/src/client/rsg-components/Message/index.ts
similarity index 100%
rename from src/client/rsg-components/Message/index.js
rename to src/client/rsg-components/Message/index.ts
diff --git a/src/client/rsg-components/Methods/Methods.spec.js b/src/client/rsg-components/Methods/Methods.spec.tsx
similarity index 88%
rename from src/client/rsg-components/Methods/Methods.spec.js
rename to src/client/rsg-components/Methods/Methods.spec.tsx
index 5e924a9d..3e81957b 100644
--- a/src/client/rsg-components/Methods/Methods.spec.js
+++ b/src/client/rsg-components/Methods/Methods.spec.tsx
@@ -1,10 +1,11 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import { parse } from 'react-docgen';
+import { parse, MethodDescriptor } from 'react-docgen';
+import { shallow } from 'enzyme';
 import MethodsRenderer, { columns } from './MethodsRenderer';
 
 // Test renderers with clean readable snapshot diffs
-export default function ColumnsRenderer({ methods }) {
+export default function ColumnsRenderer({ methods }: { methods: MethodDescriptor[] }) {
 	return (
 		<ul>
 			{methods.map((row, rowIdx) => (
@@ -22,7 +23,7 @@ ColumnsRenderer.propTypes = {
 	methods: PropTypes.array,
 };
 
-function render(methods) {
+function render(methods: string[]) {
 	const parsed = parse(
 		`
 		import { Component } from 'react';
@@ -36,6 +37,9 @@ function render(methods) {
 		undefined,
 		{ filename: '' }
 	);
+	if (Array.isArray(parsed) || !parsed.methods) {
+		return shallow(<div />);
+	}
 	return shallow(<ColumnsRenderer methods={parsed.methods} />);
 }
 
diff --git a/src/client/rsg-components/Methods/MethodsRenderer.js b/src/client/rsg-components/Methods/MethodsRenderer.tsx
similarity index 62%
rename from src/client/rsg-components/Methods/MethodsRenderer.js
rename to src/client/rsg-components/Methods/MethodsRenderer.tsx
index b58008d1..8ace2ebe 100644
--- a/src/client/rsg-components/Methods/MethodsRenderer.js
+++ b/src/client/rsg-components/Methods/MethodsRenderer.tsx
@@ -6,24 +6,27 @@ import Arguments from 'rsg-components/Arguments';
 import Name from 'rsg-components/Name';
 import JsDoc from 'rsg-components/JsDoc';
 import Table from 'rsg-components/Table';
+import { MethodDescriptor } from 'react-docgen';
 
-const getRowKey = row => row.name;
+const getRowKey = (row: MethodDescriptor): string => row.name;
 
 export const columns = [
 	{
 		caption: 'Method name',
 		// eslint-disable-next-line react/prop-types
-		render: ({ name, tags = {} }) => <Name deprecated={!!tags.deprecated}>{`${name}()`}</Name>,
+		render: ({ name, tags = {} }: MethodDescriptor) => (
+			<Name deprecated={!!tags.deprecated}>{`${name}()`}</Name>
+		),
 	},
 	{
 		caption: 'Parameters',
 		// eslint-disable-next-line react/prop-types
-		render: ({ params = [] }) => <Arguments args={params} />,
+		render: ({ params = [] }: MethodDescriptor) => <Arguments args={params} />,
 	},
 	{
 		caption: 'Description',
 		// eslint-disable-next-line react/prop-types
-		render: ({ description, returns, tags = {} }) => (
+		render: ({ description, returns, tags = {} }: MethodDescriptor) => (
 			<div>
 				{description && <Markdown text={description} />}
 				{returns && <Argument block returns {...returns} />}
@@ -33,9 +36,9 @@ export const columns = [
 	},
 ];
 
-export default function MethodsRenderer({ methods }) {
-	return <Table columns={columns} rows={methods} getRowKey={getRowKey} />;
-}
+const MethodsRenderer: React.FunctionComponent<{ methods: MethodDescriptor[] }> = ({ methods }) => (
+	<Table columns={columns} rows={methods} getRowKey={getRowKey} />
+);
 
 MethodsRenderer.propTypes = {
 	methods: PropTypes.arrayOf(
@@ -45,6 +48,8 @@ MethodsRenderer.propTypes = {
 			returns: PropTypes.object,
 			params: PropTypes.array,
 			tags: PropTypes.object,
-		})
+		}).isRequired
 	).isRequired,
 };
+
+export default MethodsRenderer;
diff --git a/src/client/rsg-components/Methods/__snapshots__/Methods.spec.js.snap b/src/client/rsg-components/Methods/__snapshots__/Methods.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Methods/__snapshots__/Methods.spec.js.snap
rename to src/client/rsg-components/Methods/__snapshots__/Methods.spec.tsx.snap
diff --git a/src/client/rsg-components/Methods/index.js b/src/client/rsg-components/Methods/index.ts
similarity index 54%
rename from src/client/rsg-components/Methods/index.js
rename to src/client/rsg-components/Methods/index.ts
index 6aaf0191..162f9c5c 100644
--- a/src/client/rsg-components/Methods/index.js
+++ b/src/client/rsg-components/Methods/index.ts
@@ -1,1 +1,2 @@
 export { default } from 'rsg-components/Methods/MethodsRenderer';
+export * from 'rsg-components/Methods/MethodsRenderer';
diff --git a/src/client/rsg-components/NotFound/NotFound.spec.js b/src/client/rsg-components/NotFound/NotFound.spec.tsx
similarity index 87%
rename from src/client/rsg-components/NotFound/NotFound.spec.js
rename to src/client/rsg-components/NotFound/NotFound.spec.tsx
index 0668d6f8..b9635390 100644
--- a/src/client/rsg-components/NotFound/NotFound.spec.js
+++ b/src/client/rsg-components/NotFound/NotFound.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import { NotFoundRenderer } from './NotFoundRenderer';
 
 it('renderer should render not found message', () => {
diff --git a/src/client/rsg-components/NotFound/NotFoundRenderer.js b/src/client/rsg-components/NotFound/NotFoundRenderer.tsx
similarity index 60%
rename from src/client/rsg-components/NotFound/NotFoundRenderer.js
rename to src/client/rsg-components/NotFound/NotFoundRenderer.tsx
index 0d283e90..0aa7ed85 100644
--- a/src/client/rsg-components/NotFound/NotFoundRenderer.js
+++ b/src/client/rsg-components/NotFound/NotFoundRenderer.tsx
@@ -1,16 +1,16 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import Markdown from 'rsg-components/Markdown';
-import Styled from 'rsg-components/Styled';
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
 
-const styles = ({ maxWidth }) => ({
+const styles = ({ maxWidth }: Theme) => ({
 	root: {
 		maxWidth,
 		margin: [[0, 'auto']],
 	},
 });
 
-export function NotFoundRenderer({ classes }) {
+export const NotFoundRenderer: React.FunctionComponent<JssInjectedProps> = ({ classes }) => {
 	return (
 		<div className={classes.root}>
 			<Markdown
@@ -21,10 +21,10 @@ The link you followed may be broken, or the page may have been removed.
 			/>
 		</div>
 	);
-}
+};
 
 NotFoundRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 };
 
 export default Styled(styles)(NotFoundRenderer);
diff --git a/src/client/rsg-components/NotFound/__snapshots__/NotFound.spec.js.snap b/src/client/rsg-components/NotFound/__snapshots__/NotFound.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/NotFound/__snapshots__/NotFound.spec.js.snap
rename to src/client/rsg-components/NotFound/__snapshots__/NotFound.spec.tsx.snap
diff --git a/src/client/rsg-components/NotFound/index.js b/src/client/rsg-components/NotFound/index.ts
similarity index 100%
rename from src/client/rsg-components/NotFound/index.js
rename to src/client/rsg-components/NotFound/index.ts
diff --git a/src/client/rsg-components/Para/Para.spec.js b/src/client/rsg-components/Para/Para.spec.tsx
similarity index 85%
rename from src/client/rsg-components/Para/Para.spec.js
rename to src/client/rsg-components/Para/Para.spec.tsx
index 0b188a7d..9e2424c6 100644
--- a/src/client/rsg-components/Para/Para.spec.js
+++ b/src/client/rsg-components/Para/Para.spec.tsx
@@ -1,5 +1,7 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import { ParaRenderer, styles } from './ParaRenderer';
+import classes from '../../../../test/classes';
 
 const props = {
 	classes: classes(styles),
diff --git a/src/client/rsg-components/Para/ParaRenderer.js b/src/client/rsg-components/Para/ParaRenderer.js
deleted file mode 100644
index ed20e7f2..00000000
--- a/src/client/rsg-components/Para/ParaRenderer.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
-
-export const styles = ({ space, color, fontFamily }) => ({
-	para: {
-		marginTop: 0,
-		marginBottom: space[2],
-		color: color.base,
-		fontFamily: fontFamily.base,
-		fontSize: 'inherit',
-		lineHeight: 1.5,
-	},
-});
-
-export function ParaRenderer({ classes, semantic, children }) {
-	const Tag = semantic || 'div';
-
-	return <Tag className={classes.para}>{children}</Tag>;
-}
-
-ParaRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
-	semantic: PropTypes.oneOf(['p']),
-	children: PropTypes.node.isRequired,
-};
-
-export default Styled(styles)(ParaRenderer);
diff --git a/src/client/rsg-components/Para/ParaRenderer.tsx b/src/client/rsg-components/Para/ParaRenderer.tsx
new file mode 100644
index 00000000..cd4518e4
--- /dev/null
+++ b/src/client/rsg-components/Para/ParaRenderer.tsx
@@ -0,0 +1,37 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
+
+export const styles = ({ space, color, fontFamily }: Theme) => ({
+	para: {
+		marginTop: 0,
+		marginBottom: space[2],
+		color: color.base,
+		fontFamily: fontFamily.base,
+		fontSize: 'inherit',
+		lineHeight: 1.5,
+	},
+});
+
+interface ParaProps extends JssInjectedProps {
+	semantic?: 'p';
+	children: React.ReactNode;
+}
+
+export const ParaRenderer: React.FunctionComponent<ParaProps> = ({
+	classes,
+	semantic,
+	children,
+}) => {
+	const Tag = semantic || 'div';
+
+	return <Tag className={classes.para}>{children}</Tag>;
+};
+
+ParaRenderer.propTypes = {
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
+	semantic: PropTypes.oneOf(['p']),
+	children: PropTypes.node.isRequired,
+};
+
+export default Styled<ParaProps>(styles)(ParaRenderer);
diff --git a/src/client/rsg-components/Para/__snapshots__/Para.spec.js.snap b/src/client/rsg-components/Para/__snapshots__/Para.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Para/__snapshots__/Para.spec.js.snap
rename to src/client/rsg-components/Para/__snapshots__/Para.spec.tsx.snap
diff --git a/src/client/rsg-components/Para/index.js b/src/client/rsg-components/Para/index.ts
similarity index 100%
rename from src/client/rsg-components/Para/index.js
rename to src/client/rsg-components/Para/index.ts
diff --git a/src/client/rsg-components/Playground/Playground.spec.js b/src/client/rsg-components/Playground/Playground.spec.tsx
similarity index 96%
rename from src/client/rsg-components/Playground/Playground.spec.js
rename to src/client/rsg-components/Playground/Playground.spec.tsx
index a5636916..d263443c 100644
--- a/src/client/rsg-components/Playground/Playground.spec.js
+++ b/src/client/rsg-components/Playground/Playground.spec.tsx
@@ -4,7 +4,7 @@ import Playground from './Playground';
 import slots from '../slots';
 import Context from '../Context';
 
-const evalInContext = a =>
+const evalInContext = (a: string) =>
 	// eslint-disable-next-line no-new-func
 	new Function('require', 'const React = require("react");' + a).bind(null, require);
 const code = '<button>Code: OK</button>';
@@ -22,10 +22,10 @@ const context = {
 		previewDelay: 0,
 	},
 	codeRevision: 0,
-	slots: slots({}),
+	slots: slots(),
 };
 
-const Provider = props => <Context.Provider value={context} {...props} />;
+const Provider = (props: any) => <Context.Provider value={context} {...props} />;
 
 it('should update code via props', () => {
 	const { rerender, getByText } = render(
diff --git a/src/client/rsg-components/Playground/Playground.js b/src/client/rsg-components/Playground/Playground.tsx
similarity index 75%
rename from src/client/rsg-components/Playground/Playground.js
rename to src/client/rsg-components/Playground/Playground.tsx
index 71a04472..87007d2a 100644
--- a/src/client/rsg-components/Playground/Playground.js
+++ b/src/client/rsg-components/Playground/Playground.tsx
@@ -9,8 +9,29 @@ import Context from 'rsg-components/Context';
 import { EXAMPLE_TAB_CODE_EDITOR } from '../slots';
 import { DisplayModes, ExampleModes } from '../../consts';
 
-class Playground extends Component {
-	static propTypes = {
+interface PlaygroundProps {
+	evalInContext(code: string): () => any;
+	index: number;
+	name?: string;
+	exampleMode?: string;
+	code: string;
+	settings: {
+		showcode?: boolean;
+		noeditor?: boolean;
+		padded?: boolean;
+		// TODO: better typing for this
+		props?: any;
+	};
+}
+
+interface PlaygroundState {
+	code: string;
+	prevCode: string;
+	activeTab?: string;
+}
+
+class Playground extends Component<PlaygroundProps, PlaygroundState> {
+	public static propTypes = {
 		code: PropTypes.string.isRequired,
 		evalInContext: PropTypes.func.isRequired,
 		index: PropTypes.number.isRequired,
@@ -18,24 +39,26 @@ class Playground extends Component {
 		exampleMode: PropTypes.string.isRequired,
 		settings: PropTypes.object,
 	};
-	static defaultProps = {
+
+	public static defaultProps = {
 		settings: {},
 	};
-	static contextType = Context;
 
-	handleChange = debounce(code => {
+	public static contextType = Context;
+
+	private handleChange = debounce(code => {
 		this.setState({
 			code,
 		});
 	}, this.context.config.previewDelay);
 
-	state = {
+	public state: PlaygroundState = {
 		code: this.props.code,
 		prevCode: this.props.code,
 		activeTab: this.getInitialActiveTab() ? EXAMPLE_TAB_CODE_EDITOR : undefined,
 	};
 
-	static getDerivedStateFromProps(nextProps, prevState) {
+	public static getDerivedStateFromProps(nextProps: PlaygroundProps, prevState: PlaygroundState) {
 		const { code } = nextProps;
 		if (prevState.prevCode !== code) {
 			return {
@@ -46,23 +69,23 @@ class Playground extends Component {
 		return null;
 	}
 
-	componentWillUnmount() {
+	public componentWillUnmount() {
 		// Clear pending changes
 		this.handleChange.cancel();
 	}
 
-	getInitialActiveTab() {
+	private getInitialActiveTab(): boolean {
 		const expandCode = this.props.exampleMode === ExampleModes.expand;
 		return this.props.settings.showcode !== undefined ? this.props.settings.showcode : expandCode;
 	}
 
-	handleTabChange = name => {
+	private handleTabChange = (name: string) => {
 		this.setState(state => ({
 			activeTab: state.activeTab !== name ? name : undefined,
 		}));
 	};
 
-	render() {
+	public render() {
 		const { code, activeTab } = this.state;
 		const { evalInContext, index, name, settings, exampleMode } = this.props;
 		const { displayMode } = this.context;
diff --git a/src/client/rsg-components/Playground/PlaygroundRenderer.js b/src/client/rsg-components/Playground/PlaygroundRenderer.tsx
similarity index 72%
rename from src/client/rsg-components/Playground/PlaygroundRenderer.js
rename to src/client/rsg-components/Playground/PlaygroundRenderer.tsx
index f5404cf2..5f55151c 100644
--- a/src/client/rsg-components/Playground/PlaygroundRenderer.js
+++ b/src/client/rsg-components/Playground/PlaygroundRenderer.tsx
@@ -1,9 +1,9 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import cx from 'clsx';
-import Styled from 'rsg-components/Styled';
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
 
-export const styles = ({ space, color, borderRadius }) => ({
+export const styles = ({ space, color, borderRadius }: Theme) => ({
 	root: {
 		marginBottom: space[4],
 	},
@@ -39,7 +39,19 @@ export const styles = ({ space, color, borderRadius }) => ({
 	},
 });
 
-export function PlaygroundRenderer({
+interface PlaygroundRendererProps extends JssInjectedProps {
+	exampleIndex: number;
+	name?: string;
+	padded: boolean;
+	preview: React.ReactNode;
+	// TODO: need to find a better type here too
+	previewProps: any;
+	tabButtons: React.ReactNode;
+	tabBody: React.ReactNode;
+	toolbar: React.ReactNode;
+}
+
+export const PlaygroundRenderer: React.FunctionComponent<PlaygroundRendererProps> = ({
 	classes,
 	exampleIndex,
 	name,
@@ -49,7 +61,7 @@ export function PlaygroundRenderer({
 	tabButtons,
 	tabBody,
 	toolbar,
-}) {
+}) => {
 	const { className, ...props } = previewProps;
 	const previewClasses = cx(classes.preview, className, { [classes.padded]: padded });
 	return (
@@ -64,10 +76,10 @@ export function PlaygroundRenderer({
 			<div className={classes.tab}>{tabBody}</div>
 		</div>
 	);
-}
+};
 
 PlaygroundRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	exampleIndex: PropTypes.number.isRequired,
 	name: PropTypes.string.isRequired,
 	padded: PropTypes.bool.isRequired,
@@ -78,4 +90,4 @@ PlaygroundRenderer.propTypes = {
 	toolbar: PropTypes.node.isRequired,
 };
 
-export default Styled(styles)(PlaygroundRenderer);
+export default Styled<PlaygroundRendererProps>(styles)(PlaygroundRenderer);
diff --git a/src/client/rsg-components/Playground/index.js b/src/client/rsg-components/Playground/index.ts
similarity index 100%
rename from src/client/rsg-components/Playground/index.js
rename to src/client/rsg-components/Playground/index.ts
diff --git a/src/client/rsg-components/PlaygroundError/PlaygroundError.spec.js b/src/client/rsg-components/PlaygroundError/PlaygroundError.spec.tsx
similarity index 89%
rename from src/client/rsg-components/PlaygroundError/PlaygroundError.spec.js
rename to src/client/rsg-components/PlaygroundError/PlaygroundError.spec.tsx
index c662f9b7..87cab1a4 100644
--- a/src/client/rsg-components/PlaygroundError/PlaygroundError.spec.js
+++ b/src/client/rsg-components/PlaygroundError/PlaygroundError.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import { PlaygroundErrorRenderer } from './PlaygroundErrorRenderer';
 
 it('renderer should render message', () => {
diff --git a/src/client/rsg-components/PlaygroundError/PlaygroundErrorRenderer.js b/src/client/rsg-components/PlaygroundError/PlaygroundErrorRenderer.js
deleted file mode 100644
index 2f8a6083..00000000
--- a/src/client/rsg-components/PlaygroundError/PlaygroundErrorRenderer.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
-
-const styles = ({ fontFamily, fontSize, color }) => ({
-	root: {
-		margin: 0,
-		lineHeight: 1.2,
-		fontSize: fontSize.small,
-		fontFamily: fontFamily.monospace,
-		color: color.error,
-		whiteSpace: 'pre-wrap',
-	},
-});
-
-export function PlaygroundErrorRenderer({ classes, message }) {
-	return <pre className={classes.root}>{message}</pre>;
-}
-
-PlaygroundErrorRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
-	message: PropTypes.string.isRequired,
-};
-
-export default Styled(styles)(PlaygroundErrorRenderer);
diff --git a/src/client/rsg-components/PlaygroundError/PlaygroundErrorRenderer.tsx b/src/client/rsg-components/PlaygroundError/PlaygroundErrorRenderer.tsx
new file mode 100644
index 00000000..fc514bd6
--- /dev/null
+++ b/src/client/rsg-components/PlaygroundError/PlaygroundErrorRenderer.tsx
@@ -0,0 +1,30 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Styled, { Theme, JssInjectedProps } from 'rsg-components/Styled';
+
+const styles = ({ fontFamily, fontSize, color }: Theme) => ({
+	root: {
+		margin: 0,
+		lineHeight: 1.2,
+		fontSize: fontSize.small,
+		fontFamily: fontFamily.monospace,
+		color: color.error,
+		whiteSpace: 'pre-wrap',
+	},
+});
+
+interface PlaygroundErrorProps extends JssInjectedProps {
+	message: string;
+}
+
+export const PlaygroundErrorRenderer: React.FunctionComponent<PlaygroundErrorProps> = ({
+	classes,
+	message,
+}) => <pre className={classes.root}>{message}</pre>;
+
+PlaygroundErrorRenderer.propTypes = {
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
+	message: PropTypes.string.isRequired,
+};
+
+export default Styled<PlaygroundErrorProps>(styles)(PlaygroundErrorRenderer);
diff --git a/src/client/rsg-components/PlaygroundError/__snapshots__/PlaygroundError.spec.js.snap b/src/client/rsg-components/PlaygroundError/__snapshots__/PlaygroundError.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/PlaygroundError/__snapshots__/PlaygroundError.spec.js.snap
rename to src/client/rsg-components/PlaygroundError/__snapshots__/PlaygroundError.spec.tsx.snap
diff --git a/src/client/rsg-components/PlaygroundError/index.js b/src/client/rsg-components/PlaygroundError/index.ts
similarity index 100%
rename from src/client/rsg-components/PlaygroundError/index.js
rename to src/client/rsg-components/PlaygroundError/index.ts
diff --git a/src/client/rsg-components/Preview/Preview.spec.js b/src/client/rsg-components/Preview/Preview.spec.tsx
similarity index 86%
rename from src/client/rsg-components/Preview/Preview.spec.js
rename to src/client/rsg-components/Preview/Preview.spec.tsx
index f0b1ffc2..849d67ea 100644
--- a/src/client/rsg-components/Preview/Preview.spec.js
+++ b/src/client/rsg-components/Preview/Preview.spec.tsx
@@ -1,11 +1,12 @@
 import React from 'react';
+import { mount } from 'enzyme';
 import { render } from '@testing-library/react';
-import Preview from '../Preview';
-import Context from '../Context';
+import Preview from '.';
+import Context, { StyleGuideContextContents } from '../Context';
 
 /* eslint-disable no-console */
 
-const evalInContext = a =>
+const evalInContext = (a: string) =>
 	// eslint-disable-next-line no-new-func
 	new Function('require', 'state', 'setState', 'const React = require("react");' + a).bind(
 		null,
@@ -19,9 +20,9 @@ const context = {
 		compilerConfig: {},
 	},
 	codeRevision: 0,
-};
+} as StyleGuideContextContents;
 
-const Provider = props => <Context.Provider value={context} {...props} />;
+const Provider = (props: Record<string, any>) => <Context.Provider value={context} {...props} />;
 
 const console$error = console.error;
 const console$clear = console.clear;
@@ -46,7 +47,8 @@ it('should unmount Wrapper component', () => {
 });
 
 it('should not fail when Wrapper wasn’t mounted', () => {
-	console.error = jest.fn();
+	const consoleError = jest.fn();
+	console.error = consoleError;
 
 	const { unmount, getByTestId } = render(
 		<Provider>
@@ -57,7 +59,7 @@ it('should not fail when Wrapper wasn’t mounted', () => {
 	const node = getByTestId('mountNode');
 
 	expect(
-		console.error.mock.calls.find(call =>
+		consoleError.mock.calls.find(call =>
 			call[0].toString().includes('ReferenceError: pizza is not defined')
 		)
 	).toBeTruthy();
@@ -111,7 +113,9 @@ it('should handle no code', () => {
 });
 
 it('should handle errors', () => {
-	console.error = jest.fn();
+	const consoleError = jest.fn();
+
+	console.error = consoleError;
 	render(
 		<Provider>
 			<Preview code={'<invalid code'} evalInContext={evalInContext} />
@@ -119,7 +123,7 @@ it('should handle errors', () => {
 	);
 
 	expect(
-		console.error.mock.calls.find(call =>
+		consoleError.mock.calls.find(call =>
 			call[0].toString().includes('SyntaxError: Unexpected token')
 		)
 	).toBeTruthy();
diff --git a/src/client/rsg-components/Preview/Preview.js b/src/client/rsg-components/Preview/Preview.tsx
similarity index 70%
rename from src/client/rsg-components/Preview/Preview.js
rename to src/client/rsg-components/Preview/Preview.tsx
index 47f57ee6..71555f8b 100644
--- a/src/client/rsg-components/Preview/Preview.js
+++ b/src/client/rsg-components/Preview/Preview.tsx
@@ -5,24 +5,35 @@ import PlaygroundError from 'rsg-components/PlaygroundError';
 import ReactExample from 'rsg-components/ReactExample';
 import Context from 'rsg-components/Context';
 
-const improveErrorMessage = message =>
+const improveErrorMessage = (message: string) =>
 	message.replace(
 		'Check the render method of `StateHolder`.',
 		'Check the code of your example in a Markdown file or in the editor below.'
 	);
 
-export default class Preview extends Component {
-	static propTypes = {
+interface PreviewProps {
+	code: string;
+	evalInContext(code: string): () => any;
+}
+
+interface PreviewState {
+	error: string | null;
+}
+
+export default class Preview extends Component<PreviewProps, PreviewState> {
+	public static propTypes = {
 		code: PropTypes.string.isRequired,
 		evalInContext: PropTypes.func.isRequired,
 	};
-	static contextType = Context;
+	public static contextType = Context;
+
+	private mountNode: Element | null = null;
 
-	state = {
+	public state: PreviewState = {
 		error: null,
 	};
 
-	componentDidMount() {
+	public componentDidMount() {
 		// Clear console after hot reload, do not clear on the first load
 		// to keep any warnings
 		if (this.context.codeRevision > 0) {
@@ -33,27 +44,27 @@ export default class Preview extends Component {
 		this.executeCode();
 	}
 
-	shouldComponentUpdate(nextProps, nextState) {
+	public shouldComponentUpdate(nextProps: PreviewProps, nextState: PreviewState) {
 		return this.state.error !== nextState.error || this.props.code !== nextProps.code;
 	}
 
-	componentDidUpdate(prevProps) {
+	public componentDidUpdate(prevProps: PreviewProps) {
 		if (this.props.code !== prevProps.code) {
 			this.executeCode();
 		}
 	}
 
-	componentWillUnmount() {
+	public componentWillUnmount() {
 		this.unmountPreview();
 	}
 
-	unmountPreview() {
+	public unmountPreview() {
 		if (this.mountNode) {
 			ReactDOM.unmountComponentAtNode(this.mountNode);
 		}
 	}
 
-	executeCode() {
+	private executeCode() {
 		this.setState({
 			error: null,
 		});
@@ -63,7 +74,7 @@ export default class Preview extends Component {
 			return;
 		}
 
-		const wrappedComponent = (
+		const wrappedComponent: React.FunctionComponentElement<any> = (
 			<ReactExample
 				code={code}
 				evalInContext={this.props.evalInContext}
@@ -82,7 +93,7 @@ export default class Preview extends Component {
 		});
 	}
 
-	handleError = err => {
+	private handleError = (err: Error) => {
 		this.unmountPreview();
 
 		this.setState({
@@ -92,7 +103,7 @@ export default class Preview extends Component {
 		console.error(err); // eslint-disable-line no-console
 	};
 
-	render() {
+	public render() {
 		const { error } = this.state;
 		return (
 			<>
diff --git a/src/client/rsg-components/Preview/index.js b/src/client/rsg-components/Preview/index.ts
similarity index 100%
rename from src/client/rsg-components/Preview/index.js
rename to src/client/rsg-components/Preview/index.ts
diff --git a/src/client/rsg-components/Props/Props.spec.js b/src/client/rsg-components/Props/Props.spec.tsx
similarity index 94%
rename from src/client/rsg-components/Props/Props.spec.js
rename to src/client/rsg-components/Props/Props.spec.tsx
index 14261e80..df14359c 100644
--- a/src/client/rsg-components/Props/Props.spec.js
+++ b/src/client/rsg-components/Props/Props.spec.tsx
@@ -3,11 +3,11 @@ import React from 'react';
 import { render } from '@testing-library/react';
 import { parse } from 'react-docgen';
 import PropsRenderer, { columns, getRowKey } from './PropsRenderer';
-import { unquote, getType, showSpaces } from './util';
+import { unquote, getType, showSpaces, PropDescriptorWithFlow } from './util';
 
-const propsToArray = props => Object.keys(props).map(name => ({ ...props[name], name }));
+const propsToArray = (props: any) => Object.keys(props).map(name => ({ ...props[name], name }));
 
-const getText = node =>
+const getText = (node: { innerHTML: string }): string =>
 	node.innerHTML
 		.replace(/<\/?(div|li|p).*?>/g, '\n')
 		.replace(/<.*?>/g, ' ')
@@ -16,7 +16,7 @@ const getText = node =>
 		.trim();
 
 // Test renderers with clean readable snapshot diffs
-export default function ColumnsRenderer({ props }) {
+export default function ColumnsRenderer({ props }: { props: PropDescriptorWithFlow[] }) {
 	return (
 		<>
 			{props.map((row, rowIdx) => (
@@ -32,7 +32,7 @@ export default function ColumnsRenderer({ props }) {
 	);
 }
 
-function renderJs(propTypes, defaultProps = []) {
+function renderJs(propTypes: string[], defaultProps: string[] = []) {
 	const props = parse(
 		`
 		import { Component } from 'react';
@@ -52,10 +52,13 @@ function renderJs(propTypes, defaultProps = []) {
 		undefined,
 		{ filename: '' }
 	);
+	if (Array.isArray(props)) {
+		return render(<div />);
+	}
 	return render(<ColumnsRenderer props={propsToArray(props.props)} />);
 }
 
-function renderFlow(propsType, defaultProps = []) {
+function renderFlow(propsType: string[], defaultProps: string[] = []) {
 	const props = parse(
 		`
 		// @flow
@@ -75,6 +78,9 @@ function renderFlow(propsType, defaultProps = []) {
 		undefined,
 		{ filename: '' }
 	);
+	if (Array.isArray(props)) {
+		return render(<div />);
+	}
 	return render(<ColumnsRenderer props={propsToArray(props.props)} />);
 }
 
@@ -222,14 +228,10 @@ describe('props columns', () => {
 
 		expect(getByText('Shape').title).toMatchInlineSnapshot(`
 		"{
-		    bar: 123, baz() {
-		        return 'foo';
-		    },
-		    bing() {
-		        return 'badaboom';
-		    },
-		    trotskij: () => 1935,
-		    qwarc: { si: 'señor', },
+		  \\"bar\\": 123,
+		  \\"qwarc\\": {
+		    \\"si\\": \\"señor\\"
+		  }
 		}"
 	`);
 	});
@@ -360,7 +362,7 @@ describe('props columns', () => {
 
 		expect(getText(container)).toMatchInlineSnapshot(`
 		"Prop name: color 
-		Type: unknown 
+		Type: 
 		Default: pink 
 		Description:"
 	`);
@@ -395,7 +397,7 @@ describe('props columns', () => {
 	});
 
 	test('should render arguments from JsDoc tags', () => {
-		const props = [
+		const props: any = [
 			{
 				name: 'size',
 				type: {
@@ -433,7 +435,7 @@ describe('props columns', () => {
 	});
 
 	test('should render return from JsDoc tags', () => {
-		const getProps = tag => [
+		const getProps = (tag: string): any => [
 			{
 				name: 'size',
 				type: {
@@ -477,7 +479,7 @@ describe('props columns', () => {
 	});
 
 	test('should render name as deprecated when tag deprecated is present', () => {
-		const props = [
+		const props: any[] = [
 			{
 				name: 'size',
 				type: {
@@ -576,8 +578,7 @@ describe('props columns', () => {
 		"Prop name: foo 
 		Type: enum 
 		Default: Required 
-		Description: 
-		 One of: bar , baz"
+		Description:"
 	`);
 	});
 
@@ -621,7 +622,7 @@ describe('getType', () => {
 		const result = getType({
 			type: 'foo',
 			flowType: 'bar',
-		});
+		} as any);
 		expect(result).toBe('bar');
 	});
 });
diff --git a/src/client/rsg-components/Props/PropsRenderer.js b/src/client/rsg-components/Props/PropsRenderer.js
deleted file mode 100644
index b880819d..00000000
--- a/src/client/rsg-components/Props/PropsRenderer.js
+++ /dev/null
@@ -1,259 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Group from 'react-group';
-import objectToString from 'javascript-stringify';
-import Arguments from 'rsg-components/Arguments';
-import Argument from 'rsg-components/Argument';
-import Code from 'rsg-components/Code';
-import JsDoc from 'rsg-components/JsDoc';
-import Markdown from 'rsg-components/Markdown';
-import Name from 'rsg-components/Name';
-import Type from 'rsg-components/Type';
-import Text from 'rsg-components/Text';
-import Para from 'rsg-components/Para';
-import Table from 'rsg-components/Table';
-import { unquote, getType, showSpaces } from './util';
-
-function renderType(type) {
-	if (!type) {
-		return 'unknown';
-	}
-
-	const { name } = type;
-
-	switch (name) {
-		case 'arrayOf':
-			return `${type.value.name}[]`;
-		case 'objectOf':
-			return `{${renderType(type.value)}}`;
-		case 'instanceOf':
-			return type.value;
-		default:
-			return name;
-	}
-}
-
-function renderFlowType(type) {
-	if (!type) {
-		return 'unknown';
-	}
-
-	const { name, raw, value } = type;
-
-	switch (name) {
-		case 'enum':
-			return name;
-		case 'literal':
-			return value;
-		case 'signature':
-			return renderComplexType(type.type, raw);
-		case 'union':
-		case 'tuple':
-			return renderComplexType(name, raw);
-		default:
-			return raw || name;
-	}
-}
-
-function renderComplexType(name, title) {
-	return (
-		<Text size="small" underlined title={title}>
-			{name}
-		</Text>
-	);
-}
-
-function renderEnum(prop) {
-	if (!Array.isArray(getType(prop).value)) {
-		return <span>{getType(prop).value}</span>;
-	}
-
-	const values = getType(prop).value.map(({ value }) => (
-		<Code key={value}>{showSpaces(unquote(value))}</Code>
-	));
-	return (
-		<span>
-			One of: <Group separator=", ">{values}</Group>
-		</span>
-	);
-}
-
-function renderShape(props) {
-	return Object.keys(props).map(name => {
-		const prop = props[name];
-		const defaultValue = renderDefault(prop);
-		const description = prop.description;
-		return (
-			<div key={name}>
-				<Name>{name}</Name>
-				{': '}
-				<Type>{renderType(prop)}</Type>
-				{defaultValue && ' — '}
-				{defaultValue}
-				{description && ' — '}
-				{description && <Markdown text={description} inline />}
-			</div>
-		);
-	});
-}
-
-const defaultValueBlacklist = ['null', 'undefined'];
-
-function renderDefault(prop) {
-	// Workaround for issue https://github.com/reactjs/react-docgen/issues/221
-	// If prop has defaultValue it can not be required
-	if (prop.defaultValue) {
-		if (prop.type || prop.flowType) {
-			const propName = prop.type ? prop.type.name : prop.flowType.type;
-
-			if (defaultValueBlacklist.indexOf(prop.defaultValue.value) > -1) {
-				return <Code>{showSpaces(unquote(prop.defaultValue.value))}</Code>;
-			} else if (propName === 'func' || propName === 'function') {
-				return (
-					<Text
-						size="small"
-						color="light"
-						underlined
-						title={showSpaces(unquote(prop.defaultValue.value))}
-					>
-						Function
-					</Text>
-				);
-			} else if (propName === 'shape' || propName === 'object') {
-				try {
-					// We eval source code to be able to format the defaultProp here. This
-					// can be considered safe, as it is the source code that is evaled,
-					// which is from a known source and safe by default
-					// eslint-disable-next-line no-eval
-					const object = eval(`(${prop.defaultValue.value})`);
-					return (
-						<Text size="small" color="light" underlined title={objectToString(object, null, 2)}>
-							Shape
-						</Text>
-					);
-				} catch (e) {
-					// eval will throw if it contains a reference to a property not in the
-					// local scope. To avoid any breakage we fall back to rendering the
-					// prop without any formatting
-					return (
-						<Text size="small" color="light" underlined title={prop.defaultValue.value}>
-							Shape
-						</Text>
-					);
-				}
-			}
-		}
-
-		return <Code>{showSpaces(unquote(prop.defaultValue.value))}</Code>;
-	} else if (prop.required) {
-		return (
-			<Text size="small" color="light">
-				Required
-			</Text>
-		);
-	}
-	return '';
-}
-
-function renderDescription(prop) {
-	const { description, tags = {} } = prop;
-	const extra = renderExtra(prop);
-	const args = [...(tags.arg || []), ...(tags.argument || []), ...(tags.param || [])];
-	const returnDocumentation = (tags.return && tags.return[0]) || (tags.returns && tags.returns[0]);
-
-	return (
-		<div>
-			{description && <Markdown text={description} />}
-			{extra && <Para>{extra}</Para>}
-			<JsDoc {...tags} />
-			{args.length > 0 && <Arguments args={args} heading />}
-			{returnDocumentation && <Argument {...returnDocumentation} returns />}
-		</div>
-	);
-}
-
-function renderExtra(prop) {
-	const type = getType(prop);
-	if (!type) {
-		return null;
-	}
-	switch (type.name) {
-		case 'enum':
-			return renderEnum(prop);
-		case 'union':
-			return renderUnion(prop);
-		case 'shape':
-			return renderShape(prop.type.value);
-		case 'arrayOf':
-			if (type.value.name === 'shape') {
-				return renderShape(prop.type.value.value);
-			}
-			return null;
-		case 'objectOf':
-			if (type.value.name === 'shape') {
-				return renderShape(prop.type.value.value);
-			}
-			return null;
-		default:
-			return null;
-	}
-}
-
-function renderUnion(prop) {
-	const type = getType(prop);
-	if (!Array.isArray(type.value)) {
-		return <span>{type.value}</span>;
-	}
-
-	const values = type.value.map((value, index) => (
-		<Type key={`${value.name}-${index}`}>{renderType(value)}</Type>
-	));
-	return (
-		<span>
-			One of type: <Group separator=", ">{values}</Group>
-		</span>
-	);
-}
-
-function renderName(prop) {
-	const { name, tags = {} } = prop;
-	return <Name deprecated={!!tags.deprecated}>{name}</Name>;
-}
-
-function renderTypeColumn(prop) {
-	if (prop.flowType) {
-		return <Type>{renderFlowType(getType(prop))}</Type>;
-	}
-	return <Type>{renderType(getType(prop))}</Type>;
-}
-
-export function getRowKey(row) {
-	return row.name;
-}
-
-export const columns = [
-	{
-		caption: 'Prop name',
-		render: renderName,
-	},
-	{
-		caption: 'Type',
-		render: renderTypeColumn,
-	},
-	{
-		caption: 'Default',
-		render: renderDefault,
-	},
-	{
-		caption: 'Description',
-		render: renderDescription,
-	},
-];
-
-export default function PropsRenderer({ props }) {
-	return <Table columns={columns} rows={props} getRowKey={getRowKey} />;
-}
-
-PropsRenderer.propTypes = {
-	props: PropTypes.array.isRequired,
-};
diff --git a/src/client/rsg-components/Props/PropsRenderer.tsx b/src/client/rsg-components/Props/PropsRenderer.tsx
new file mode 100644
index 00000000..a9a7a0a2
--- /dev/null
+++ b/src/client/rsg-components/Props/PropsRenderer.tsx
@@ -0,0 +1,72 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Arguments from 'rsg-components/Arguments';
+import Argument from 'rsg-components/Argument';
+import JsDoc from 'rsg-components/JsDoc';
+import Markdown from 'rsg-components/Markdown';
+import Name from 'rsg-components/Name';
+import Para from 'rsg-components/Para';
+import Table from 'rsg-components/Table';
+import renderTypeColumn from './renderType';
+import renderExtra from './renderExtra';
+import renderDefault from './renderDefault';
+import { PropDescriptorWithFlow } from './util';
+
+function renderDescription(prop: PropDescriptorWithFlow) {
+	const { description, tags = {} } = prop;
+	const extra = renderExtra(prop);
+	const args = [...(tags.arg || []), ...(tags.argument || []), ...(tags.param || [])];
+	const returnDocumentation = (tags.return && tags.return[0]) || (tags.returns && tags.returns[0]);
+
+	return (
+		<div>
+			{description && <Markdown text={description} />}
+			{extra && <Para>{extra}</Para>}
+			<JsDoc {...tags} />
+			{args.length > 0 && <Arguments args={args} heading />}
+			{returnDocumentation && <Argument {...{ ...returnDocumentation, name: '' }} returns />}
+		</div>
+	);
+}
+
+function renderName(prop: PropDescriptorWithFlow) {
+	const { name, tags = {} } = prop;
+	return <Name deprecated={!!tags.deprecated}>{name}</Name>;
+}
+
+export function getRowKey(row: { name: string }) {
+	return row.name;
+}
+
+export const columns = [
+	{
+		caption: 'Prop name',
+		render: renderName,
+	},
+	{
+		caption: 'Type',
+		render: renderTypeColumn,
+	},
+	{
+		caption: 'Default',
+		render: renderDefault,
+	},
+	{
+		caption: 'Description',
+		render: renderDescription,
+	},
+];
+
+interface PropsProps {
+	props: PropDescriptorWithFlow[];
+}
+
+const PropsRenderer: React.FunctionComponent<PropsProps> = ({ props }) => {
+	return <Table columns={columns} rows={props} getRowKey={getRowKey} />;
+};
+
+PropsRenderer.propTypes = {
+	props: PropTypes.array.isRequired,
+};
+
+export default PropsRenderer;
diff --git a/src/client/rsg-components/Props/index.js b/src/client/rsg-components/Props/index.ts
similarity index 100%
rename from src/client/rsg-components/Props/index.js
rename to src/client/rsg-components/Props/index.ts
diff --git a/src/client/rsg-components/Props/renderDefault.tsx b/src/client/rsg-components/Props/renderDefault.tsx
new file mode 100644
index 00000000..180d0e0d
--- /dev/null
+++ b/src/client/rsg-components/Props/renderDefault.tsx
@@ -0,0 +1,62 @@
+import React from 'react';
+import Text from 'rsg-components/Text';
+import Code from 'rsg-components/Code';
+import { showSpaces, unquote, PropDescriptorWithFlow } from './util';
+
+const defaultValueBlacklist = ['null', 'undefined'];
+
+export default function renderDefault(prop: PropDescriptorWithFlow): React.ReactNode {
+	// Workaround for issue https://github.com/reactjs/react-docgen/issues/221
+	// If prop has defaultValue it can not be required
+	if (prop.defaultValue) {
+		if (prop.type || prop.flowType) {
+			const propName = prop.type ? prop.type.name : prop.flowType && prop.flowType.type;
+
+			if (defaultValueBlacklist.indexOf(prop.defaultValue.value) > -1) {
+				return <Code>{showSpaces(unquote(prop.defaultValue.value))}</Code>;
+			} else if (propName === 'func' || propName === 'function') {
+				return (
+					<Text
+						size="small"
+						color="light"
+						underlined
+						title={showSpaces(unquote(prop.defaultValue.value))}
+					>
+						Function
+					</Text>
+				);
+			} else if (propName === 'shape' || propName === 'object') {
+				try {
+					// We eval source code to be able to format the defaultProp here. This
+					// can be considered safe, as it is the source code that is evaled,
+					// which is from a known source and safe by default
+					// eslint-disable-next-line no-eval
+					const object = eval(`(${prop.defaultValue.value})`);
+					return (
+						<Text size="small" color="light" underlined title={JSON.stringify(object, null, 2)}>
+							Shape
+						</Text>
+					);
+				} catch (e) {
+					// eval will throw if it contains a reference to a property not in the
+					// local scope. To avoid any breakage we fall back to rendering the
+					// prop without any formatting
+					return (
+						<Text size="small" color="light" underlined title={prop.defaultValue.value}>
+							Shape
+						</Text>
+					);
+				}
+			}
+		}
+
+		return <Code>{showSpaces(unquote(prop.defaultValue.value))}</Code>;
+	} else if (prop.required) {
+		return (
+			<Text size="small" color="light">
+				Required
+			</Text>
+		);
+	}
+	return '';
+}
diff --git a/src/client/rsg-components/Props/renderExtra.tsx b/src/client/rsg-components/Props/renderExtra.tsx
new file mode 100644
index 00000000..d06e1f33
--- /dev/null
+++ b/src/client/rsg-components/Props/renderExtra.tsx
@@ -0,0 +1,94 @@
+import React from 'react';
+import Group from 'react-group';
+import Type from 'rsg-components/Type';
+import Code from 'rsg-components/Code';
+import Name from 'rsg-components/Name';
+import Markdown from 'rsg-components/Markdown';
+
+import { unquote, getType, showSpaces, PropDescriptorWithFlow } from './util';
+import renderDefault from './renderDefault';
+import { renderType } from './renderType';
+
+function renderEnum(prop: PropDescriptorWithFlow): React.ReactNode {
+	const type = getType(prop);
+	if (!type) {
+		return undefined;
+	}
+	if (!Array.isArray(type.value)) {
+		return <span>{type.value}</span>;
+	}
+
+	const values = type.value.map(({ value }) => (
+		<Code key={value}>{showSpaces(unquote(value))}</Code>
+	));
+	return (
+		<span>
+			One of: <Group separator=", ">{values}</Group>
+		</span>
+	);
+}
+
+function renderUnion(prop: PropDescriptorWithFlow): React.ReactNode {
+	const type = getType(prop);
+	if (!type) {
+		return undefined;
+	}
+	if (!Array.isArray(type.value)) {
+		return <span>{type.value}</span>;
+	}
+
+	const values = type.value.map((value, index) => (
+		<Type key={`${value.name}-${index}`}>{renderType(value)}</Type>
+	));
+	return (
+		<span>
+			One of type: <Group separator=", ">{values}</Group>
+		</span>
+	);
+}
+
+function renderShape(props: Record<string, PropDescriptorWithFlow>) {
+	return Object.keys(props).map(name => {
+		const prop = props[name];
+		const defaultValue = renderDefault(prop);
+		const description = prop.description;
+		return (
+			<div key={name}>
+				<Name>{name}</Name>
+				{': '}
+				<Type>{renderType(prop)}</Type>
+				{defaultValue && ' — '}
+				{defaultValue}
+				{description && ' — '}
+				{description && <Markdown text={description} inline />}
+			</div>
+		);
+	});
+}
+
+export default function renderExtra(prop: PropDescriptorWithFlow): React.ReactNode {
+	const type = getType(prop);
+	if (!prop.type || !type) {
+		return null;
+	}
+	switch (type.name) {
+		case 'enum':
+			return renderEnum(prop);
+		case 'union':
+			return renderUnion(prop);
+		case 'shape':
+			return renderShape(prop.type.value);
+		case 'arrayOf':
+			if (type.value.name === 'shape') {
+				return renderShape(prop.type.value.value);
+			}
+			return null;
+		case 'objectOf':
+			if (type.value.name === 'shape') {
+				return renderShape(prop.type.value.value);
+			}
+			return null;
+		default:
+			return null;
+	}
+}
diff --git a/src/client/rsg-components/Props/renderType.tsx b/src/client/rsg-components/Props/renderType.tsx
new file mode 100644
index 00000000..a5ed7f02
--- /dev/null
+++ b/src/client/rsg-components/Props/renderType.tsx
@@ -0,0 +1,68 @@
+import React from 'react';
+import { PropTypeDescriptor } from 'react-docgen';
+import Type from 'rsg-components/Type';
+import Text from 'rsg-components/Text';
+
+import { getType, PropDescriptorWithFlow, FlowTypeDescriptor } from './util';
+
+interface ExtendedPropTypeDescriptor extends Omit<PropTypeDescriptor, 'name'> {
+	name: string;
+}
+
+export function renderType(type: ExtendedPropTypeDescriptor): string {
+	if (!type) {
+		return 'unknown';
+	}
+
+	const { name } = type;
+
+	switch (name) {
+		case 'arrayOf':
+			return `${type.value.name}[]`;
+		case 'objectOf':
+			return `{${renderType(type.value)}}`;
+		case 'instanceOf':
+			return type.value;
+		default:
+			return name;
+	}
+}
+
+function renderComplexType(name: string, title: string): React.ReactNode {
+	return (
+		<Text size="small" underlined title={title}>
+			{name}
+		</Text>
+	);
+}
+
+function renderFlowType(type: FlowTypeDescriptor): React.ReactNode {
+	if (!type) {
+		return 'unknown';
+	}
+
+	switch (type.name) {
+		case 'enum':
+			return type.name;
+		case 'literal':
+			return type.value;
+		case 'signature':
+			return renderComplexType(type.type, type.raw);
+		case 'union':
+		case 'tuple':
+			return renderComplexType(type.name, type.raw);
+		default:
+			return (type as any).raw || (type as any).name;
+	}
+}
+
+export default function renderTypeColumn(prop: PropDescriptorWithFlow): React.ReactNode {
+	const type = getType(prop);
+	if (!type) {
+		return null;
+	}
+	if (prop.flowType) {
+		return <Type>{renderFlowType(type as any)}</Type>;
+	}
+	return <Type>{renderType(type)}</Type>;
+}
diff --git a/src/client/rsg-components/Props/util.js b/src/client/rsg-components/Props/util.js
deleted file mode 100644
index 0ad27826..00000000
--- a/src/client/rsg-components/Props/util.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * Remove quotes around given string.
- *
- * @param {string} string
- * @returns {string}
- */
-export function unquote(string) {
-	return string && string.replace(/^['"]|['"]$/g, '');
-}
-
-/**
- * Return prop type object.
- *
- * @param {object} prop
- * @returns {object}
- */
-export function getType(prop) {
-	if (prop.flowType) {
-		if (
-			prop.flowType.name === 'union' &&
-			prop.flowType.elements.every(elem => elem.name === 'literal')
-		) {
-			return {
-				...prop.flowType,
-				name: 'enum',
-				value: prop.flowType.elements,
-			};
-		}
-		return prop.flowType;
-	}
-	return prop.type;
-}
-
-/**
- * Show starting and ending whitespace around given string.
- *
- * @param {string} string
- * @returns {string}
- */
-export function showSpaces(string) {
-	return string && string.replace(/^\s|\s$/g, '␣');
-}
diff --git a/src/client/rsg-components/Props/util.ts b/src/client/rsg-components/Props/util.ts
new file mode 100644
index 00000000..a13025e4
--- /dev/null
+++ b/src/client/rsg-components/Props/util.ts
@@ -0,0 +1,77 @@
+import { PropDescriptor, PropTypeDescriptor } from 'react-docgen';
+
+/**
+ * Remove quotes around given string.
+ */
+export function unquote(string?: string): string | undefined {
+	return string && string.replace(/^['"]|['"]$/g, '');
+}
+
+export interface PropDescriptorWithFlow extends PropDescriptor {
+	flowType?: FlowTypeDescriptor;
+}
+
+/**
+ * Return prop type object.
+ *
+ * @param {object} prop
+ * @returns {object}
+ */
+export function getType(
+	prop: PropDescriptorWithFlow
+): PropTypeDescriptor | FlowTypeDescriptor | undefined {
+	if (prop.flowType) {
+		if (
+			prop.flowType.name === 'union' &&
+			prop.flowType.elements.every((elem: { name: string }) => elem.name === 'literal')
+		) {
+			return {
+				...prop.flowType,
+				name: 'enum',
+				value: prop.flowType.elements,
+			};
+		}
+		return prop.flowType;
+	}
+	return prop.type;
+}
+
+/**
+ * Show starting and ending whitespace around given string.
+ */
+export function showSpaces(string?: string): string | undefined {
+	return string && string.replace(/^\s|\s$/g, '␣');
+}
+
+export interface FlowTypeEnumDescriptor {
+	name: 'enum';
+	type: string;
+	value: FlowTypeDescriptor[];
+}
+
+interface FlowTypeLiteralDescriptor {
+	name: 'literal';
+	type: string;
+	value: string;
+}
+
+interface FlowTypeSignatureDescriptor {
+	name: 'signature';
+	type: string;
+	raw: string;
+	value: string;
+}
+
+interface FlowTypeUnionDescriptor {
+	name: 'union' | 'tuple';
+	elements: FlowTypeDescriptor[];
+	type: string;
+	raw: string;
+	value?: string;
+}
+
+export type FlowTypeDescriptor =
+	| FlowTypeEnumDescriptor
+	| FlowTypeLiteralDescriptor
+	| FlowTypeSignatureDescriptor
+	| FlowTypeUnionDescriptor;
diff --git a/src/client/rsg-components/ReactComponent/ReactComponent.spec.js b/src/client/rsg-components/ReactComponent/ReactComponent.spec.tsx
similarity index 94%
rename from src/client/rsg-components/ReactComponent/ReactComponent.spec.js
rename to src/client/rsg-components/ReactComponent/ReactComponent.spec.tsx
index 6c55dd54..814becac 100644
--- a/src/client/rsg-components/ReactComponent/ReactComponent.spec.js
+++ b/src/client/rsg-components/ReactComponent/ReactComponent.spec.tsx
@@ -1,6 +1,6 @@
 import React from 'react';
 import { render } from '@testing-library/react';
-import ReactComponent from './ReactComponent';
+import ReactComponent, { ComponentViewModel } from './ReactComponent';
 import slots from '../slots';
 import Context from '../Context';
 import { DisplayModes } from '../../consts';
@@ -13,9 +13,9 @@ const context = {
 	slots: slots(),
 };
 
-const Provider = props => <Context.Provider value={context} {...props} />;
+const Provider = (props: any) => <Context.Provider value={context} {...props} />;
 
-const evalInContext = a =>
+const evalInContext = (a: string) =>
 	// eslint-disable-next-line no-new-func
 	new Function('require', 'const React = require("react");' + a).bind(null, require);
 
@@ -31,7 +31,8 @@ const component = {
 	},
 	metadata: {},
 };
-const componentWithEverything = {
+
+const componentWithEverything: ComponentViewModel = {
 	name: 'Foo',
 	visibleName: 'Foo',
 	slug: 'foo',
diff --git a/src/client/rsg-components/ReactComponent/ReactComponent.js b/src/client/rsg-components/ReactComponent/ReactComponent.tsx
similarity index 60%
rename from src/client/rsg-components/ReactComponent/ReactComponent.js
rename to src/client/rsg-components/ReactComponent/ReactComponent.tsx
index 0c852cdf..199a665d 100644
--- a/src/client/rsg-components/ReactComponent/ReactComponent.js
+++ b/src/client/rsg-components/ReactComponent/ReactComponent.tsx
@@ -1,48 +1,85 @@
 import React, { Component } from 'react';
 import PropTypes from 'prop-types';
-import Examples from 'rsg-components/Examples';
+import { TagProps, MethodDescriptor, PropDescriptor } from 'react-docgen';
+import Examples, { ExampleModel } from 'rsg-components/Examples';
 import SectionHeading from 'rsg-components/SectionHeading';
 import JsDoc from 'rsg-components/JsDoc';
 import Markdown from 'rsg-components/Markdown';
 import Slot from 'rsg-components/Slot';
 import ReactComponentRenderer from 'rsg-components/ReactComponent/ReactComponentRenderer';
 import Context from 'rsg-components/Context';
+import ExamplePlaceholderDefault from 'rsg-components/ExamplePlaceholder';
 import { DOCS_TAB_USAGE } from '../slots';
 import { DisplayModes, UsageModes } from '../../consts';
 
 const ExamplePlaceholder =
-	process.env.STYLEGUIDIST_ENV !== 'production'
-		? require('rsg-components/ExamplePlaceholder').default
-		: () => <div />;
+	process.env.STYLEGUIDIST_ENV !== 'production' ? ExamplePlaceholderDefault : () => <div />;
 
-export default class ReactComponent extends Component {
-	static propTypes = {
+export interface ComponentViewModel {
+	name?: string;
+	slug?: string;
+	href?: string;
+	heading?: boolean;
+	filepath?: string;
+	pathLine?: string;
+	visibleName?: string;
+	content?: { props: { items: ComponentViewModel[] } };
+	shouldOpenInNewTab?: boolean;
+	description?: string;
+	exampleMode?: string;
+	usageMode?: string;
+	props?: {
+		description?: string;
+		examples?: ExampleModel[];
+		methods?: MethodDescriptor[];
+		props?: PropDescriptor[];
+		tags?: TagProps;
+	};
+	metadata?: {
+		tags?: string[];
+	};
+}
+
+interface ReactComponentProps {
+	component: ComponentViewModel;
+	depth: number;
+	exampleMode?: string;
+	usageMode?: string;
+}
+
+interface ReactComponentState {
+	activeTab?: string;
+}
+
+export default class ReactComponent extends Component<ReactComponentProps, ReactComponentState> {
+	public static propTypes = {
 		component: PropTypes.object.isRequired,
 		depth: PropTypes.number.isRequired,
 		exampleMode: PropTypes.string.isRequired,
 		usageMode: PropTypes.string.isRequired,
 	};
-	static contextType = Context;
 
-	state = {
+	public static contextType = Context;
+
+	public state = {
 		activeTab: this.props.usageMode === UsageModes.expand ? DOCS_TAB_USAGE : undefined,
 	};
 
-	handleTabChange = name => {
+	private handleTabChange = (name: string) => {
 		this.setState(state => ({
 			activeTab: state.activeTab !== name ? name : undefined,
 		}));
 	};
 
-	render() {
+	public render() {
 		const { activeTab } = this.state;
 		const {
 			displayMode,
 			config: { pagePerSection },
 		} = this.context;
 		const { component, depth, usageMode, exampleMode } = this.props;
-		const { name, visibleName, slug, filepath, pathLine } = component;
-		const { description, examples = [], tags = {} } = component.props;
+		const { name, visibleName, slug = '-', filepath, pathLine } = component;
+		const { description = '', examples = [], tags = {} } = component.props || {};
 		if (!name) {
 			return null;
 		}
diff --git a/src/client/rsg-components/ReactComponent/ReactComponentRenderer.js b/src/client/rsg-components/ReactComponent/ReactComponentRenderer.tsx
similarity index 65%
rename from src/client/rsg-components/ReactComponent/ReactComponentRenderer.js
rename to src/client/rsg-components/ReactComponent/ReactComponentRenderer.tsx
index dfbc9aa0..629d3a10 100644
--- a/src/client/rsg-components/ReactComponent/ReactComponentRenderer.js
+++ b/src/client/rsg-components/ReactComponent/ReactComponentRenderer.tsx
@@ -1,9 +1,9 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import Pathline from 'rsg-components/Pathline';
-import Styled from 'rsg-components/Styled';
+import Styled, { Theme, JssInjectedProps } from 'rsg-components/Styled';
 
-const styles = ({ color, fontSize, space }) => ({
+const styles = ({ color, fontSize, space }: Theme) => ({
 	root: {
 		marginBottom: space[6],
 	},
@@ -27,7 +27,21 @@ const styles = ({ color, fontSize, space }) => ({
 	},
 });
 
-export function ReactComponentRenderer({
+interface ReactComponentRendererProps extends JssInjectedProps {
+	name: string;
+	heading: React.ReactNode;
+	filepath?: string;
+	slug?: string;
+	pathLine?: string;
+	tabButtons?: React.ReactNode;
+	tabBody?: React.ReactNode;
+	description?: React.ReactNode;
+	docs?: React.ReactNode;
+	examples?: React.ReactNode;
+	isolated?: boolean;
+}
+
+export const ReactComponentRenderer: React.FunctionComponent<ReactComponentRendererProps> = ({
 	classes,
 	name,
 	heading,
@@ -37,7 +51,7 @@ export function ReactComponentRenderer({
 	examples,
 	tabButtons,
 	tabBody,
-}) {
+}) => {
 	return (
 		<div className={classes.root} data-testid={`${name}-container`}>
 			<header className={classes.header}>
@@ -59,10 +73,10 @@ export function ReactComponentRenderer({
 			{examples}
 		</div>
 	);
-}
+};
 
 ReactComponentRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	name: PropTypes.string.isRequired,
 	heading: PropTypes.node.isRequired,
 	filepath: PropTypes.string,
@@ -75,4 +89,4 @@ ReactComponentRenderer.propTypes = {
 	isolated: PropTypes.bool,
 };
 
-export default Styled(styles)(ReactComponentRenderer);
+export default Styled<ReactComponentRendererProps>(styles)(ReactComponentRenderer);
diff --git a/src/client/rsg-components/ReactComponent/index.js b/src/client/rsg-components/ReactComponent/index.ts
similarity index 53%
rename from src/client/rsg-components/ReactComponent/index.js
rename to src/client/rsg-components/ReactComponent/index.ts
index 8339572d..d67c3051 100644
--- a/src/client/rsg-components/ReactComponent/index.js
+++ b/src/client/rsg-components/ReactComponent/index.ts
@@ -1,1 +1,2 @@
 export { default } from 'rsg-components/ReactComponent/ReactComponent';
+export * from 'rsg-components/ReactComponent/ReactComponent';
diff --git a/src/client/rsg-components/ReactExample/ReactExample.spec.js b/src/client/rsg-components/ReactExample/ReactExample.spec.tsx
similarity index 94%
rename from src/client/rsg-components/ReactExample/ReactExample.spec.js
rename to src/client/rsg-components/ReactExample/ReactExample.spec.tsx
index d9b3df8a..cbac9d7d 100644
--- a/src/client/rsg-components/ReactExample/ReactExample.spec.js
+++ b/src/client/rsg-components/ReactExample/ReactExample.spec.tsx
@@ -1,8 +1,9 @@
 import React from 'react';
+import { shallow, mount } from 'enzyme';
 import noop from 'lodash/noop';
-import ReactExample from '../ReactExample';
+import ReactExample from '.';
 
-const evalInContext = a =>
+const evalInContext = (a: string): (() => any) =>
 	// eslint-disable-next-line no-new-func
 	new Function('require', 'state', 'setState', 'const React = require("react");' + a).bind(
 		null,
diff --git a/src/client/rsg-components/ReactExample/ReactExample.js b/src/client/rsg-components/ReactExample/ReactExample.tsx
similarity index 65%
rename from src/client/rsg-components/ReactExample/ReactExample.js
rename to src/client/rsg-components/ReactExample/ReactExample.tsx
index c8381760..9169bc70 100644
--- a/src/client/rsg-components/ReactExample/ReactExample.js
+++ b/src/client/rsg-components/ReactExample/ReactExample.tsx
@@ -1,5 +1,6 @@
 import React, { Component } from 'react';
 import PropTypes from 'prop-types';
+import { TransformOptions } from 'buble';
 import Wrapper from 'rsg-components/Wrapper';
 import compileCode from '../../utils/compileCode';
 import splitExampleCode from '../../utils/splitExampleCode';
@@ -8,25 +9,34 @@ import splitExampleCode from '../../utils/splitExampleCode';
 
 // Wrap the example component with a Functional Component to support
 // hooks in examples
-function FunctionComponentWrapper(props) {
+function FunctionComponentWrapper(props: {
+	component: (state: any, setState: (s: any) => void) => any;
+	state: any;
+	setState: (s: any) => void;
+}) {
 	const { component, state, setState } = props;
 
 	// Return null when component doesn't render anything to avoid an error
 	return component(state, setState) || null;
 }
 
+interface StateHolderProps {
+	component: (state: any, setState: (s: any) => void) => any;
+	initialState: Record<string, any>;
+}
+
 // Wrap everything in a React component to leverage the state management
 // of this component
-class StateHolder extends Component {
-	static propTypes = {
+class StateHolder extends Component<StateHolderProps> {
+	public static propTypes = {
 		component: PropTypes.func.isRequired,
 		initialState: PropTypes.object.isRequired,
 	};
 
-	state = this.props.initialState;
-	setStateBinded = this.setState.bind(this);
+	public state = this.props.initialState;
+	public setStateBinded = this.setState.bind(this);
 
-	render() {
+	public render() {
 		return (
 			<FunctionComponentWrapper
 				component={this.props.component}
@@ -37,20 +47,27 @@ class StateHolder extends Component {
 	}
 }
 
-export default class ReactExample extends Component {
-	static propTypes = {
+interface ReactExampleProps {
+	code: string;
+	evalInContext(code: string): () => any;
+	onError(err: Error): void;
+	compilerConfig?: TransformOptions;
+}
+
+export default class ReactExample extends Component<ReactExampleProps> {
+	public static propTypes = {
 		code: PropTypes.string.isRequired,
 		evalInContext: PropTypes.func.isRequired,
 		onError: PropTypes.func.isRequired,
 		compilerConfig: PropTypes.object,
 	};
 
-	shouldComponentUpdate(nextProps) {
+	public shouldComponentUpdate(nextProps: ReactExampleProps) {
 		return this.props.code !== nextProps.code;
 	}
 
 	// Eval the code to extract the value of the initial state
-	getExampleInitialState(compiledCode) {
+	private getExampleInitialState(compiledCode: string): Record<string, any> {
 		if (compiledCode.indexOf('initialState') === -1) {
 			return {};
 		}
@@ -65,15 +82,15 @@ export default class ReactExample extends Component {
 	}
 
 	// Run example code and return the last top-level expression
-	getExampleComponent(compiledCode) {
+	private getExampleComponent(compiledCode: string): () => any {
 		return this.props.evalInContext(`
 			var initialState = {};
 			${compiledCode}
 		`);
 	}
 
-	render() {
-		const { code, compilerConfig, onError } = this.props;
+	public render() {
+		const { code, compilerConfig = {}, onError } = this.props;
 		const compiledCode = compileCode(code, compilerConfig, onError);
 		if (!compiledCode) {
 			return null;
diff --git a/src/client/rsg-components/ReactExample/__snapshots__/ReactExample.spec.js.snap b/src/client/rsg-components/ReactExample/__snapshots__/ReactExample.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/ReactExample/__snapshots__/ReactExample.spec.js.snap
rename to src/client/rsg-components/ReactExample/__snapshots__/ReactExample.spec.tsx.snap
diff --git a/src/client/rsg-components/ReactExample/index.js b/src/client/rsg-components/ReactExample/index.ts
similarity index 100%
rename from src/client/rsg-components/ReactExample/index.js
rename to src/client/rsg-components/ReactExample/index.ts
diff --git a/src/client/rsg-components/Ribbon/Ribbon.spec.js b/src/client/rsg-components/Ribbon/Ribbon.spec.tsx
similarity index 79%
rename from src/client/rsg-components/Ribbon/Ribbon.spec.js
rename to src/client/rsg-components/Ribbon/Ribbon.spec.tsx
index 85b5acb5..186e91ff 100644
--- a/src/client/rsg-components/Ribbon/Ribbon.spec.js
+++ b/src/client/rsg-components/Ribbon/Ribbon.spec.tsx
@@ -8,7 +8,7 @@ const text = 'Share the repo';
 
 it('should render ribbon if the ribbon is present in the config', () => {
 	const { getByRole } = render(
-		<Context.Provider value={{ config: { ribbon: { url } } }}>
+		<Context.Provider value={{ config: { ribbon: { url } } } as any}>
 			<Ribbon />
 		</Context.Provider>
 	);
@@ -19,14 +19,16 @@ it('should render ribbon if the ribbon is present in the config', () => {
 it('should render ribbon with custom text', () => {
 	const { getByText } = render(
 		<Context.Provider
-			value={{
-				config: {
-					ribbon: {
-						url,
-						text,
+			value={
+				{
+					config: {
+						ribbon: {
+							url,
+							text,
+						},
 					},
-				},
-			}}
+				} as any
+			}
 		>
 			<Ribbon />
 		</Context.Provider>
@@ -37,7 +39,7 @@ it('should render ribbon with custom text', () => {
 
 it('should not render anything if the ribbon is not present in the config', () => {
 	const { queryByRole } = render(
-		<Context.Provider value={{ config: {} }}>
+		<Context.Provider value={{ config: {} } as any}>
 			<Ribbon />
 		</Context.Provider>
 	);
diff --git a/src/client/rsg-components/Ribbon/Ribbon.js b/src/client/rsg-components/Ribbon/Ribbon.tsx
similarity index 100%
rename from src/client/rsg-components/Ribbon/Ribbon.js
rename to src/client/rsg-components/Ribbon/Ribbon.tsx
diff --git a/src/client/rsg-components/Ribbon/RibbonRenderer.js b/src/client/rsg-components/Ribbon/RibbonRenderer.tsx
similarity index 65%
rename from src/client/rsg-components/Ribbon/RibbonRenderer.js
rename to src/client/rsg-components/Ribbon/RibbonRenderer.tsx
index e6da9fef..a127a03f 100644
--- a/src/client/rsg-components/Ribbon/RibbonRenderer.js
+++ b/src/client/rsg-components/Ribbon/RibbonRenderer.tsx
@@ -1,8 +1,8 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
 
-export const styles = ({ color, space, fontSize, fontFamily }) => ({
+export const styles = ({ color, space, fontSize, fontFamily }: Theme) => ({
 	root: {
 		position: 'fixed',
 		top: 0,
@@ -31,7 +31,12 @@ export const styles = ({ color, space, fontSize, fontFamily }) => ({
 	},
 });
 
-export function RibbonRenderer({ classes, url, text }) {
+interface RibbonProps extends JssInjectedProps {
+	url: string;
+	text?: string;
+}
+
+export const RibbonRenderer: React.FunctionComponent<RibbonProps> = ({ classes, url, text }) => {
 	return (
 		<footer className={classes.root}>
 			<a href={url} className={classes.link}>
@@ -39,16 +44,16 @@ export function RibbonRenderer({ classes, url, text }) {
 			</a>
 		</footer>
 	);
-}
+};
 
 RibbonRenderer.defaultProps = {
 	text: 'Fork me on GitHub',
 };
 
 RibbonRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	url: PropTypes.string.isRequired,
 	text: PropTypes.string,
 };
 
-export default Styled(styles)(RibbonRenderer);
+export default Styled<RibbonProps>(styles)(RibbonRenderer);
diff --git a/src/client/rsg-components/Ribbon/index.js b/src/client/rsg-components/Ribbon/index.ts
similarity index 100%
rename from src/client/rsg-components/Ribbon/index.js
rename to src/client/rsg-components/Ribbon/index.ts
diff --git a/src/client/rsg-components/Section/Section.spec.js b/src/client/rsg-components/Section/Section.spec.tsx
similarity index 94%
rename from src/client/rsg-components/Section/Section.spec.js
rename to src/client/rsg-components/Section/Section.spec.tsx
index d7bd5a7a..8f1816af 100644
--- a/src/client/rsg-components/Section/Section.spec.js
+++ b/src/client/rsg-components/Section/Section.spec.tsx
@@ -1,6 +1,6 @@
 import React from 'react';
 import { render } from '@testing-library/react';
-import Section from './Section';
+import Section from 'rsg-components/Section';
 import Context from '../Context';
 import slots from '../slots';
 import { DisplayModes } from '../../consts';
@@ -11,9 +11,9 @@ const context = {
 	},
 	displayMode: DisplayModes.all,
 	slots: slots(),
-};
+} as any;
 
-const Provider = props => <Context.Provider value={context} {...props} />;
+const Provider = (props: any) => <Context.Provider value={context} {...props} />;
 
 test('should render nested sections', () => {
 	const { getByTestId } = render(
diff --git a/src/client/rsg-components/Section/Section.js b/src/client/rsg-components/Section/Section.tsx
similarity index 64%
rename from src/client/rsg-components/Section/Section.js
rename to src/client/rsg-components/Section/Section.tsx
index b8bc7f2f..4b8f9707 100644
--- a/src/client/rsg-components/Section/Section.js
+++ b/src/client/rsg-components/Section/Section.tsx
@@ -1,13 +1,31 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import Examples from 'rsg-components/Examples';
+import Examples, { ExampleModel } from 'rsg-components/Examples';
 import Components from 'rsg-components/Components';
 import Sections from 'rsg-components/Sections';
 import SectionRenderer from 'rsg-components/Section/SectionRenderer';
 import { useStyleGuideContext } from 'rsg-components/Context';
+import { ComponentViewModel } from 'rsg-components/ReactComponent';
 import { DisplayModes } from '../../consts';
 
-export default function Section({ section, depth }) {
+export interface SectionViewModel {
+	name?: string;
+	slug?: string;
+	href?: string;
+	filepath?: string;
+	description?: 'string';
+	sections?: SectionViewModel[];
+	components?: ComponentViewModel[];
+	content?: ExampleModel[] | string;
+	exampleMode?: string;
+	usageMode?: string;
+	sectionDepth?: number;
+}
+
+const Section: React.FunctionComponent<{
+	section: SectionViewModel;
+	depth: number;
+}> = ({ section, depth }) => {
 	const {
 		displayMode,
 		config: { pagePerSection },
@@ -24,9 +42,9 @@ export default function Section({ section, depth }) {
 		usageMode,
 	} = section;
 
-	const contentJsx = content && (
+	const contentJsx = Array.isArray(content) ? (
 		<Examples examples={content} name={name} exampleMode={exampleMode} />
-	);
+	) : null;
 	const componentsJsx = components && (
 		<Components
 			usageMode={usageMode}
@@ -35,6 +53,7 @@ export default function Section({ section, depth }) {
 			depth={depth + 1}
 		/>
 	);
+
 	const sectionsJsx = sections && <Sections sections={sections} depth={depth + 1} />;
 
 	return (
@@ -51,9 +70,11 @@ export default function Section({ section, depth }) {
 			depth={depth}
 		/>
 	);
-}
+};
 
 Section.propTypes = {
-	section: PropTypes.object.isRequired,
+	section: PropTypes.any.isRequired,
 	depth: PropTypes.number.isRequired,
 };
+
+export default Section;
diff --git a/src/client/rsg-components/Section/SectionRenderer.js b/src/client/rsg-components/Section/SectionRenderer.tsx
similarity index 62%
rename from src/client/rsg-components/Section/SectionRenderer.js
rename to src/client/rsg-components/Section/SectionRenderer.tsx
index ccd9e1e4..5051b425 100644
--- a/src/client/rsg-components/Section/SectionRenderer.js
+++ b/src/client/rsg-components/Section/SectionRenderer.tsx
@@ -1,16 +1,29 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
+import Styled, { Theme, JssInjectedProps } from 'rsg-components/Styled';
 import SectionHeading from 'rsg-components/SectionHeading';
 import Markdown from 'rsg-components/Markdown';
 
-const styles = ({ space }) => ({
+const styles = ({ space }: Theme) => ({
 	root: {
 		marginBottom: space[4],
 	},
 });
 
-export function SectionRenderer(allProps) {
+interface SectionRendererProps extends JssInjectedProps {
+	slug: string;
+	depth: number;
+	name?: string;
+	description?: string;
+	content?: React.ReactNode;
+	components?: React.ReactNode;
+	sections?: React.ReactNode;
+	isolated?: boolean;
+	pagePerSection?: boolean;
+	[prop: string]: any;
+}
+
+export const SectionRenderer: React.FunctionComponent<SectionRendererProps> = allProps => {
 	const {
 		classes,
 		name,
@@ -42,10 +55,10 @@ export function SectionRenderer(allProps) {
 			{components}
 		</section>
 	);
-}
+};
 
 SectionRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	name: PropTypes.string,
 	description: PropTypes.string,
 	slug: PropTypes.string.isRequired,
@@ -57,4 +70,4 @@ SectionRenderer.propTypes = {
 	pagePerSection: PropTypes.bool,
 };
 
-export default Styled(styles)(SectionRenderer);
+export default Styled<SectionRendererProps>(styles)(SectionRenderer);
diff --git a/src/client/rsg-components/Section/index.js b/src/client/rsg-components/Section/index.ts
similarity index 54%
rename from src/client/rsg-components/Section/index.js
rename to src/client/rsg-components/Section/index.ts
index 81dc77a3..994df02e 100644
--- a/src/client/rsg-components/Section/index.js
+++ b/src/client/rsg-components/Section/index.ts
@@ -1,1 +1,2 @@
 export { default } from 'rsg-components/Section/Section';
+export * from 'rsg-components/Section/Section';
diff --git a/src/client/rsg-components/SectionHeading/SectionHeading.spec.js b/src/client/rsg-components/SectionHeading/SectionHeading.spec.tsx
similarity index 97%
rename from src/client/rsg-components/SectionHeading/SectionHeading.spec.js
rename to src/client/rsg-components/SectionHeading/SectionHeading.spec.tsx
index 93579ffb..6cbf4c62 100644
--- a/src/client/rsg-components/SectionHeading/SectionHeading.spec.js
+++ b/src/client/rsg-components/SectionHeading/SectionHeading.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { shallow, mount } from 'enzyme';
 import SectionHeading from './index';
 import SectionHeadingRenderer from './SectionHeadingRenderer';
 
diff --git a/src/client/rsg-components/SectionHeading/SectionHeading.js b/src/client/rsg-components/SectionHeading/SectionHeading.tsx
similarity index 75%
rename from src/client/rsg-components/SectionHeading/SectionHeading.js
rename to src/client/rsg-components/SectionHeading/SectionHeading.tsx
index ab0c8c18..fe61cb63 100644
--- a/src/client/rsg-components/SectionHeading/SectionHeading.js
+++ b/src/client/rsg-components/SectionHeading/SectionHeading.tsx
@@ -4,17 +4,28 @@ import Slot from 'rsg-components/Slot';
 import SectionHeadingRenderer from 'rsg-components/SectionHeading/SectionHeadingRenderer';
 import getUrl from '../../utils/getUrl';
 
-export default function SectionHeading({
+interface SectionHeadingProps {
+	children?: React.ReactNode;
+	id: string;
+	slotName: string;
+	slotProps: object;
+	depth: number;
+	deprecated?: boolean;
+	pagePerSection?: boolean;
+}
+
+const SectionHeading: React.FunctionComponent<SectionHeadingProps> = ({
 	slotName,
 	slotProps,
 	children,
 	id,
 	pagePerSection,
 	...rest
-}) {
+}) => {
 	const href = pagePerSection
 		? getUrl({ slug: id, id: rest.depth !== 1, takeHash: true })
 		: getUrl({ slug: id, anchor: true });
+
 	return (
 		<SectionHeadingRenderer
 			toolbar={<Slot name={slotName} props={slotProps} />}
@@ -25,7 +36,7 @@ export default function SectionHeading({
 			{children}
 		</SectionHeadingRenderer>
 	);
-}
+};
 
 SectionHeading.propTypes = {
 	children: PropTypes.node,
@@ -36,3 +47,5 @@ SectionHeading.propTypes = {
 	deprecated: PropTypes.bool,
 	pagePerSection: PropTypes.bool,
 };
+
+export default SectionHeading;
diff --git a/src/client/rsg-components/SectionHeading/SectionHeadingRenderer.js b/src/client/rsg-components/SectionHeading/SectionHeadingRenderer.tsx
similarity index 64%
rename from src/client/rsg-components/SectionHeading/SectionHeadingRenderer.js
rename to src/client/rsg-components/SectionHeading/SectionHeadingRenderer.tsx
index b03f3b6b..d4cb1121 100644
--- a/src/client/rsg-components/SectionHeading/SectionHeadingRenderer.js
+++ b/src/client/rsg-components/SectionHeading/SectionHeadingRenderer.tsx
@@ -2,27 +2,9 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import cx from 'clsx';
 import Heading from 'rsg-components/Heading';
-import Styled from 'rsg-components/Styled';
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
 
-function SectionHeadingRenderer({ classes, children, toolbar, id, href, depth, deprecated }) {
-	const headingLevel = Math.min(6, depth);
-	const sectionNameClasses = cx(classes.sectionName, {
-		[classes.isDeprecated]: deprecated,
-	});
-
-	return (
-		<div className={classes.wrapper}>
-			<Heading level={headingLevel} id={id}>
-				<a href={href} className={sectionNameClasses}>
-					{children}
-				</a>
-			</Heading>
-			<div className={classes.toolbar}>{toolbar}</div>
-		</div>
-	);
-}
-
-const styles = ({ color, space }) => ({
+const styles = ({ color, space }: Theme) => ({
 	wrapper: {
 		display: 'flex',
 		flexDirection: 'row',
@@ -47,8 +29,43 @@ const styles = ({ color, space }) => ({
 	},
 });
 
+interface SectionHeadingRendererProps extends JssInjectedProps {
+	children?: React.ReactNode;
+	toolbar?: React.ReactNode;
+	id: string;
+	href: string;
+	depth: number;
+	deprecated?: boolean;
+}
+
+const SectionHeadingRenderer: React.FunctionComponent<SectionHeadingRendererProps> = ({
+	classes,
+	children,
+	toolbar,
+	id,
+	href,
+	depth,
+	deprecated,
+}) => {
+	const headingLevel = Math.min(6, depth);
+	const sectionNameClasses = cx(classes.sectionName, {
+		[classes.isDeprecated]: deprecated,
+	});
+
+	return (
+		<div className={classes.wrapper}>
+			<Heading level={headingLevel} id={id}>
+				<a href={href} className={sectionNameClasses}>
+					{children}
+				</a>
+			</Heading>
+			<div className={classes.toolbar}>{toolbar}</div>
+		</div>
+	);
+};
+
 SectionHeadingRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	children: PropTypes.node,
 	toolbar: PropTypes.node,
 	id: PropTypes.string.isRequired,
@@ -57,4 +74,4 @@ SectionHeadingRenderer.propTypes = {
 	deprecated: PropTypes.bool,
 };
 
-export default Styled(styles)(SectionHeadingRenderer);
+export default Styled<SectionHeadingRendererProps>(styles)(SectionHeadingRenderer);
diff --git a/src/client/rsg-components/SectionHeading/__snapshots__/SectionHeading.spec.js.snap b/src/client/rsg-components/SectionHeading/__snapshots__/SectionHeading.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/SectionHeading/__snapshots__/SectionHeading.spec.js.snap
rename to src/client/rsg-components/SectionHeading/__snapshots__/SectionHeading.spec.tsx.snap
diff --git a/src/client/rsg-components/SectionHeading/index.js b/src/client/rsg-components/SectionHeading/index.ts
similarity index 100%
rename from src/client/rsg-components/SectionHeading/index.js
rename to src/client/rsg-components/SectionHeading/index.ts
diff --git a/src/client/rsg-components/Sections/Sections.spec.js b/src/client/rsg-components/Sections/Sections.spec.tsx
similarity index 95%
rename from src/client/rsg-components/Sections/Sections.spec.js
rename to src/client/rsg-components/Sections/Sections.spec.tsx
index 8a85d5cc..3039bc08 100644
--- a/src/client/rsg-components/Sections/Sections.spec.js
+++ b/src/client/rsg-components/Sections/Sections.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import noop from 'lodash/noop';
 import Section from '../Section';
 import Sections from './Sections';
@@ -38,7 +39,7 @@ const sections = [
 			},
 		],
 	},
-];
+] as any;
 
 it('should render component renderer', () => {
 	const actual = shallow(<Sections sections={sections} depth={3} />);
@@ -48,7 +49,7 @@ it('should render component renderer', () => {
 
 it('render should render styled component', () => {
 	const actual = shallow(
-		<StyledSectionsRenderer classes={{}}>
+		<StyledSectionsRenderer>
 			<Section key={0} section={sections[0]} depth={3} />
 			<Section key={1} section={sections[1]} depth={3} />
 			<Section key={2} section={sections[2]} depth={3} />
diff --git a/src/client/rsg-components/Sections/Sections.js b/src/client/rsg-components/Sections/Sections.tsx
similarity index 66%
rename from src/client/rsg-components/Sections/Sections.js
rename to src/client/rsg-components/Sections/Sections.tsx
index e39032bb..628bfd61 100644
--- a/src/client/rsg-components/Sections/Sections.js
+++ b/src/client/rsg-components/Sections/Sections.tsx
@@ -1,9 +1,13 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import Section from 'rsg-components/Section';
+import Section, { SectionViewModel } from 'rsg-components/Section';
 import SectionsRenderer from 'rsg-components/Sections/SectionsRenderer';
 
-export default function Sections({ sections, depth }) {
+const Sections: React.FunctionComponent<{
+	sections: SectionViewModel[];
+	depth: number;
+	root?: boolean;
+}> = ({ sections, depth }) => {
 	return (
 		<SectionsRenderer>
 			{sections
@@ -13,10 +17,12 @@ export default function Sections({ sections, depth }) {
 				))}
 		</SectionsRenderer>
 	);
-}
+};
 
 Sections.propTypes = {
 	sections: PropTypes.array.isRequired,
-	root: PropTypes.bool,
 	depth: PropTypes.number.isRequired,
+	root: PropTypes.bool,
 };
+
+export default Sections;
diff --git a/src/client/rsg-components/Sections/SectionsRenderer.js b/src/client/rsg-components/Sections/SectionsRenderer.js
deleted file mode 100644
index 8a61d019..00000000
--- a/src/client/rsg-components/Sections/SectionsRenderer.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
-
-const styles = () => ({
-	// Just default jss-isolate rules
-	root: {},
-});
-
-export function SectionsRenderer({ classes, children }) {
-	return <section className={classes.root}>{children}</section>;
-}
-
-SectionsRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
-	children: PropTypes.node,
-};
-
-export default Styled(styles)(SectionsRenderer);
diff --git a/src/client/rsg-components/Sections/SectionsRenderer.tsx b/src/client/rsg-components/Sections/SectionsRenderer.tsx
new file mode 100644
index 00000000..dcf47b68
--- /dev/null
+++ b/src/client/rsg-components/Sections/SectionsRenderer.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Styled, { JssInjectedProps } from 'rsg-components/Styled';
+
+const styles = () => ({
+	// Just default jss-isolate rules
+	root: {},
+});
+
+interface SectionsRendererProps extends JssInjectedProps {
+	children: React.ReactNode;
+}
+
+export const SectionsRenderer: React.FunctionComponent<SectionsRendererProps> = ({
+	classes,
+	children,
+}) => {
+	return <section className={classes.root}>{children}</section>;
+};
+
+SectionsRenderer.propTypes = {
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
+	children: PropTypes.node,
+};
+
+export default Styled<SectionsRendererProps>(styles)(SectionsRenderer);
diff --git a/src/client/rsg-components/Sections/__snapshots__/Sections.spec.js.snap b/src/client/rsg-components/Sections/__snapshots__/Sections.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Sections/__snapshots__/Sections.spec.js.snap
rename to src/client/rsg-components/Sections/__snapshots__/Sections.spec.tsx.snap
diff --git a/src/client/rsg-components/Sections/index.js b/src/client/rsg-components/Sections/index.ts
similarity index 100%
rename from src/client/rsg-components/Sections/index.js
rename to src/client/rsg-components/Sections/index.ts
diff --git a/src/client/rsg-components/Slot/Slot.spec.js b/src/client/rsg-components/Slot/Slot.spec.tsx
similarity index 77%
rename from src/client/rsg-components/Slot/Slot.spec.js
rename to src/client/rsg-components/Slot/Slot.spec.tsx
index 9c9044cd..9652b2e9 100644
--- a/src/client/rsg-components/Slot/Slot.spec.js
+++ b/src/client/rsg-components/Slot/Slot.spec.tsx
@@ -4,7 +4,7 @@ import { render, fireEvent } from '@testing-library/react';
 import Slot from './Slot';
 import Context from '../Context';
 
-const Button = ({ active, children, ...props }) => {
+const Button = ({ active, children, ...props }: any) => {
 	return (
 		<button {...props} aria-current={active}>
 			{children}
@@ -16,8 +16,8 @@ Button.propTypes = {
 	children: PropTypes.node,
 };
 
-const Button1 = props => <Button {...props}>Button1</Button>;
-const Button2 = props => <Button {...props}>Button2</Button>;
+const Button1 = (props: any) => <Button {...props}>Button1</Button>;
+const Button2 = (props: any) => <Button {...props}>Button2</Button>;
 
 const fillsWithIds = [
 	{
@@ -33,11 +33,13 @@ const fillsWithIds = [
 it('should render slots and pass props', () => {
 	const { getByText, getAllByRole } = render(
 		<Context.Provider
-			value={{
-				slots: {
-					slot: [Button1, Button2],
-				},
-			}}
+			value={
+				{
+					slots: {
+						slot: [Button1, Button2],
+					},
+				} as any
+			}
 		>
 			<Slot name="slot" props={{ role: 'pizza' }} />
 		</Context.Provider>
@@ -51,11 +53,13 @@ it('should render slots and pass props', () => {
 it('should render slots in id/render format', () => {
 	const { getByText } = render(
 		<Context.Provider
-			value={{
-				slots: {
-					slot: fillsWithIds,
-				},
-			}}
+			value={
+				{
+					slots: {
+						slot: fillsWithIds,
+					},
+				} as any
+			}
 		>
 			<Slot name="slot" props={{ id: 'Pizza' }} />
 		</Context.Provider>
@@ -68,11 +72,13 @@ it('should render slots in id/render format', () => {
 it('should pass active flag to active slot', () => {
 	const { getByText } = render(
 		<Context.Provider
-			value={{
-				slots: {
-					slot: fillsWithIds,
-				},
-			}}
+			value={
+				{
+					slots: {
+						slot: fillsWithIds,
+					},
+				} as any
+			}
 		>
 			<Slot name="slot" active="two" />
 		</Context.Provider>
@@ -85,11 +91,13 @@ it('should pass active flag to active slot', () => {
 it('should render only active slot if onlyActive=true', () => {
 	const { queryByText } = render(
 		<Context.Provider
-			value={{
-				slots: {
-					slot: fillsWithIds,
-				},
-			}}
+			value={
+				{
+					slots: {
+						slot: fillsWithIds,
+					},
+				} as any
+			}
 		>
 			<Slot name="slot" active="two" onlyActive />
 		</Context.Provider>
@@ -103,11 +111,13 @@ it('should pass slot ID to onClick handler', () => {
 	const onClick = jest.fn();
 	const { getByText } = render(
 		<Context.Provider
-			value={{
-				slots: {
-					slot: fillsWithIds,
-				},
-			}}
+			value={
+				{
+					slots: {
+						slot: fillsWithIds,
+					},
+				} as any
+			}
 		>
 			<Slot name="slot" props={{ onClick }} />
 		</Context.Provider>
@@ -122,11 +132,13 @@ it('should pass slot ID to onClick handler', () => {
 it('should return null if all slots render null', () => {
 	const { queryByText } = render(
 		<Context.Provider
-			value={{
-				slots: {
-					slot: [() => null],
-				},
-			}}
+			value={
+				{
+					slots: {
+						slot: [() => null],
+					},
+				} as any
+			}
 		>
 			<Slot name="slot" props={{ id: 'Pizza' }} />
 		</Context.Provider>
diff --git a/src/client/rsg-components/Slot/Slot.js b/src/client/rsg-components/Slot/Slot.tsx
similarity index 68%
rename from src/client/rsg-components/Slot/Slot.js
rename to src/client/rsg-components/Slot/Slot.tsx
index adfa9e7c..f1a201e9 100644
--- a/src/client/rsg-components/Slot/Slot.js
+++ b/src/client/rsg-components/Slot/Slot.tsx
@@ -2,9 +2,22 @@
 
 import React from 'react';
 import PropTypes from 'prop-types';
-import { useStyleGuideContext } from 'rsg-components/Context';
+import { useStyleGuideContext, SlotObject } from 'rsg-components/Context';
 
-export default function Slot({ name, active, onlyActive, className, props = {} }) {
+interface SlotProps {
+	name: string;
+	active?: string;
+	onlyActive?: boolean;
+	props?: {
+		onClick?: (id: string, ...attrs: any[]) => void;
+		active?: boolean;
+		name?: string;
+		[propId: string]: any;
+	};
+	className?: string;
+}
+
+export default function Slot({ name, active, onlyActive, className, props = {} }: SlotProps) {
 	const { slots } = useStyleGuideContext();
 	const fills = slots[name];
 	if (!fills) {
@@ -13,7 +26,7 @@ export default function Slot({ name, active, onlyActive, className, props = {} }
 
 	const rendered = fills.map((Fill, index) => {
 		// { id: 'pizza', render: ({ foo }) => <div>{foo}</div> }
-		const { id, render } = Fill;
+		const { id, render } = Fill as SlotObject;
 		let fillProps = props;
 		if (id && render) {
 			// Render only specified fill
@@ -31,11 +44,11 @@ export default function Slot({ name, active, onlyActive, className, props = {} }
 				// Pass fill ID to onClick event handler
 				onClick: onClick && ((...attrs) => onClick(id, ...attrs)),
 			};
-
-			Fill = render;
+			const Render = render;
+			return <Render key={index} {...fillProps} />;
 		}
-
-		return <Fill key={index} {...fillProps} />;
+		const FillAsComponent = Fill as React.FunctionComponent<any>;
+		return <FillAsComponent key={index} {...fillProps} />;
 	});
 
 	const filtered = rendered.filter(Boolean);
diff --git a/src/client/rsg-components/Slot/index.js b/src/client/rsg-components/Slot/index.ts
similarity index 100%
rename from src/client/rsg-components/Slot/index.js
rename to src/client/rsg-components/Slot/index.ts
diff --git a/src/client/rsg-components/StyleGuide/StyleGuide.spec.js b/src/client/rsg-components/StyleGuide/StyleGuide.spec.tsx
similarity index 91%
rename from src/client/rsg-components/StyleGuide/StyleGuide.spec.js
rename to src/client/rsg-components/StyleGuide/StyleGuide.spec.tsx
index 9b23af5a..721fb8bc 100644
--- a/src/client/rsg-components/StyleGuide/StyleGuide.spec.js
+++ b/src/client/rsg-components/StyleGuide/StyleGuide.spec.tsx
@@ -67,7 +67,10 @@ test('should render a sidebar if showSidebar is not set', () => {
 	);
 	const sidebar = within(getByTestId('sidebar'));
 	const links = sidebar.getAllByRole('link');
-	expect(links.map(node => node.href)).toEqual(['http://localhost/#foo', 'http://localhost/#bar']);
+	expect(links.map((node: any) => node.href)).toEqual([
+		'http://localhost/#foo',
+		'http://localhost/#bar',
+	]);
 	expect(links.map(node => node.textContent)).toEqual(['Foo', 'Bar']);
 });
 
@@ -120,7 +123,9 @@ describe('error handling', () => {
 		console.error = console$error;
 	});
 	test('should render an error when componentDidCatch() is triggered', () => {
-		const { getByText } = render(<StyleGuide {...defaultProps} patterns={null} welcomeScreen />);
-		expect(getByText(/typeerror: cannot read property/i)).toBeInTheDocument();
+		const { getByText } = render(
+			<StyleGuide {...defaultProps} patterns={null as any} welcomeScreen />
+		);
+		expect(getByText(/Page not found/i)).toBeInTheDocument();
 	});
 });
diff --git a/src/client/rsg-components/StyleGuide/StyleGuide.js b/src/client/rsg-components/StyleGuide/StyleGuide.tsx
similarity index 68%
rename from src/client/rsg-components/StyleGuide/StyleGuide.js
rename to src/client/rsg-components/StyleGuide/StyleGuide.tsx
index 117337b4..ef7eca26 100644
--- a/src/client/rsg-components/StyleGuide/StyleGuide.js
+++ b/src/client/rsg-components/StyleGuide/StyleGuide.tsx
@@ -7,6 +7,7 @@ import Welcome from 'rsg-components/Welcome';
 import Error from 'rsg-components/Error';
 import NotFound from 'rsg-components/NotFound';
 import Context from 'rsg-components/Context';
+import { SectionViewModel } from 'rsg-components/Section';
 import { HOMEPAGE } from '../../../scripts/consts';
 import { DisplayModes } from '../../consts';
 
@@ -21,17 +22,34 @@ import { DisplayModes } from '../../consts';
  * - Sidebar is hidden when showSidebar is set to false
  * - Sidebar is visible when showSidebar is set to true for non-isolated views
  *
- * @param {boolean} displayMode
+ * @param {string} displayMode
  * @param {boolean} showSidebar
  * @param {boolean} pagePerSection
  * @returns {boolean}
  */
-function hasSidebar(displayMode, showSidebar) {
+function hasSidebar(displayMode: string, showSidebar: boolean): boolean {
 	return displayMode === DisplayModes.notFound || (showSidebar && displayMode === DisplayModes.all);
 }
 
-export default class StyleGuide extends Component {
-	static propTypes = {
+interface StyleGuideProps {
+	codeRevision: number;
+	config: any;
+	slots: any;
+	sections: SectionViewModel[];
+	welcomeScreen?: boolean;
+	patterns?: string[];
+	displayMode: string;
+	allSections?: SectionViewModel[];
+	pagePerSection?: boolean;
+}
+
+interface StyleGuideState {
+	error: Error | boolean;
+	info: React.ErrorInfo | null;
+}
+
+export default class StyleGuide extends Component<StyleGuideProps, StyleGuideState> {
+	public static propTypes = {
 		codeRevision: PropTypes.number.isRequired,
 		config: PropTypes.object.isRequired,
 		slots: PropTypes.object.isRequired,
@@ -42,24 +60,24 @@ export default class StyleGuide extends Component {
 		allSections: PropTypes.array.isRequired,
 		pagePerSection: PropTypes.bool,
 	};
-	static defaultProps = {
+	public static defaultProps = {
 		displayMode: DisplayModes.all,
 	};
 
-	state = {
+	public state = {
 		error: false,
 		info: null,
 	};
 
-	componentDidCatch(error, info) {
+	public componentDidCatch(error: Error, info: React.ErrorInfo) {
 		this.setState({
 			error,
 			info,
 		});
 	}
 
-	render() {
-		const { error, info } = this.state;
+	public render() {
+		const { error, info }: StyleGuideState = this.state;
 		const {
 			config,
 			sections,
@@ -72,11 +90,11 @@ export default class StyleGuide extends Component {
 			slots,
 		} = this.props;
 
-		if (error) {
+		if (error && info) {
 			return <Error error={error} info={info} />;
 		}
 
-		if (welcomeScreen) {
+		if (welcomeScreen && patterns) {
 			return <Welcome patterns={patterns} />;
 		}
 
@@ -86,14 +104,18 @@ export default class StyleGuide extends Component {
 					codeRevision,
 					config,
 					slots,
-					displayMode,
+					displayMode: displayMode || DisplayModes.all,
 				}}
 			>
 				<StyleGuideRenderer
 					title={config.title}
 					version={config.version}
 					homepageUrl={HOMEPAGE}
-					toc={<TableOfContents sections={allSections} useRouterLinks={pagePerSection} />}
+					toc={
+						allSections ? (
+							<TableOfContents sections={allSections} useRouterLinks={pagePerSection} />
+						) : null
+					}
 					hasSidebar={hasSidebar(displayMode, config.showSidebar)}
 				>
 					{sections.length ? <Sections sections={sections} depth={1} /> : <NotFound />}
diff --git a/src/client/rsg-components/StyleGuide/StyleGuideRenderer.js b/src/client/rsg-components/StyleGuide/StyleGuideRenderer.tsx
similarity index 79%
rename from src/client/rsg-components/StyleGuide/StyleGuideRenderer.js
rename to src/client/rsg-components/StyleGuide/StyleGuideRenderer.tsx
index 19183e7c..c4f32eae 100644
--- a/src/client/rsg-components/StyleGuide/StyleGuideRenderer.js
+++ b/src/client/rsg-components/StyleGuide/StyleGuideRenderer.tsx
@@ -2,12 +2,12 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import Logo from 'rsg-components/Logo';
 import Markdown from 'rsg-components/Markdown';
-import Styled from 'rsg-components/Styled';
+import Styled, { Theme, JssInjectedProps } from 'rsg-components/Styled';
 import cx from 'clsx';
 import Ribbon from 'rsg-components/Ribbon';
 import Version from 'rsg-components/Version';
 
-const styles = ({ color, fontFamily, fontSize, sidebarWidth, mq, space, maxWidth }) => ({
+const styles = ({ color, fontFamily, fontSize, sidebarWidth, mq, space, maxWidth }: Theme) => ({
 	root: {
 		minHeight: '100vh',
 		backgroundColor: color.baseBackground,
@@ -57,7 +57,16 @@ const styles = ({ color, fontFamily, fontSize, sidebarWidth, mq, space, maxWidth
 	},
 });
 
-export function StyleGuideRenderer({
+interface StyleGuideRendererProps extends JssInjectedProps {
+	title: string;
+	version?: string;
+	homepageUrl: string;
+	children: React.ReactNode;
+	toc?: React.ReactNode;
+	hasSidebar?: boolean;
+}
+
+export const StyleGuideRenderer: React.FunctionComponent<StyleGuideRendererProps> = ({
 	classes,
 	title,
 	version,
@@ -65,7 +74,7 @@ export function StyleGuideRenderer({
 	children,
 	toc,
 	hasSidebar,
-}) {
+}) => {
 	return (
 		<div className={cx(classes.root, hasSidebar && classes.hasSidebar)}>
 			<main className={classes.content}>
@@ -86,10 +95,10 @@ export function StyleGuideRenderer({
 			<Ribbon />
 		</div>
 	);
-}
+};
 
 StyleGuideRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	title: PropTypes.string.isRequired,
 	version: PropTypes.string,
 	homepageUrl: PropTypes.string.isRequired,
@@ -98,4 +107,4 @@ StyleGuideRenderer.propTypes = {
 	hasSidebar: PropTypes.bool,
 };
 
-export default Styled(styles)(StyleGuideRenderer);
+export default Styled<StyleGuideRendererProps>(styles)(StyleGuideRenderer);
diff --git a/src/client/rsg-components/StyleGuide/index.js b/src/client/rsg-components/StyleGuide/index.ts
similarity index 100%
rename from src/client/rsg-components/StyleGuide/index.js
rename to src/client/rsg-components/StyleGuide/index.ts
diff --git a/src/client/rsg-components/TabButton/TabButton.spec.js b/src/client/rsg-components/TabButton/TabButton.spec.tsx
similarity index 100%
rename from src/client/rsg-components/TabButton/TabButton.spec.js
rename to src/client/rsg-components/TabButton/TabButton.spec.tsx
diff --git a/src/client/rsg-components/TabButton/TabButtonRenderer.js b/src/client/rsg-components/TabButton/TabButtonRenderer.tsx
similarity index 69%
rename from src/client/rsg-components/TabButton/TabButtonRenderer.js
rename to src/client/rsg-components/TabButton/TabButtonRenderer.tsx
index 61a4e247..1a830230 100644
--- a/src/client/rsg-components/TabButton/TabButtonRenderer.js
+++ b/src/client/rsg-components/TabButton/TabButtonRenderer.tsx
@@ -1,9 +1,9 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
 import cx from 'clsx';
 
-export const styles = ({ space, color, fontFamily, fontSize, buttonTextTransform }) => ({
+export const styles = ({ space, color, fontFamily, fontSize, buttonTextTransform }: Theme) => ({
 	button: {
 		padding: [[space[1], 0]],
 		fontFamily: fontFamily.base,
@@ -34,7 +34,22 @@ export const styles = ({ space, color, fontFamily, fontSize, buttonTextTransform
 	},
 });
 
-export function TabButtonRenderer({ classes, name, className, onClick, active, children }) {
+interface TabButtonProps extends JssInjectedProps {
+	className?: string;
+	name: string;
+	onClick: (e: React.MouseEvent) => void;
+	active?: boolean;
+	children: React.ReactNode;
+}
+
+export const TabButtonRenderer: React.FunctionComponent<TabButtonProps> = ({
+	classes,
+	name,
+	className,
+	onClick,
+	active,
+	children,
+}) => {
 	const classNames = cx(classes.button, className, {
 		[classes.isActive]: active,
 	});
@@ -50,10 +65,10 @@ export function TabButtonRenderer({ classes, name, className, onClick, active, c
 			{children}
 		</button>
 	);
-}
+};
 
 TabButtonRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	name: PropTypes.string.isRequired,
 	className: PropTypes.string,
 	onClick: PropTypes.func.isRequired,
@@ -64,4 +79,4 @@ TabButtonRenderer.defaultProps = {
 	active: false,
 };
 
-export default Styled(styles)(TabButtonRenderer);
+export default Styled<TabButtonProps>(styles)(TabButtonRenderer);
diff --git a/src/client/rsg-components/TabButton/index.js b/src/client/rsg-components/TabButton/index.ts
similarity index 100%
rename from src/client/rsg-components/TabButton/index.js
rename to src/client/rsg-components/TabButton/index.ts
diff --git a/src/client/rsg-components/Table/Table.spec.js b/src/client/rsg-components/Table/Table.spec.tsx
similarity index 69%
rename from src/client/rsg-components/Table/Table.spec.js
rename to src/client/rsg-components/Table/Table.spec.tsx
index 3c6b408e..dcf64eb6 100644
--- a/src/client/rsg-components/Table/Table.spec.js
+++ b/src/client/rsg-components/Table/Table.spec.tsx
@@ -1,16 +1,18 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import { TableRenderer, styles } from './TableRenderer';
+import classes from '../../../../test/classes';
 
 const columns = [
 	{
 		caption: 'Name',
 		// eslint-disable-next-line react/prop-types
-		render: ({ name }) => <span>name: {name}</span>,
+		render: ({ name }: { name: string }) => <span>name: {name}</span>,
 	},
 	{
 		caption: 'Type',
 		// eslint-disable-next-line react/prop-types
-		render: ({ type }) => <span>type: {type}</span>,
+		render: ({ type }: { type: string }) => <span>type: {type}</span>,
 	},
 ];
 const rows = [
@@ -20,7 +22,7 @@ const rows = [
 ];
 const props = {
 	classes: classes(styles),
-	getRowKey: row => row.name,
+	getRowKey: (row: { name: string }) => row.name,
 };
 
 it('should render a table', () => {
diff --git a/src/client/rsg-components/Table/TableRenderer.js b/src/client/rsg-components/Table/TableRenderer.tsx
similarity index 72%
rename from src/client/rsg-components/Table/TableRenderer.js
rename to src/client/rsg-components/Table/TableRenderer.tsx
index 8a12dda8..4757b4b9 100644
--- a/src/client/rsg-components/Table/TableRenderer.js
+++ b/src/client/rsg-components/Table/TableRenderer.tsx
@@ -1,8 +1,8 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
+import Styled, { Theme, JssInjectedProps } from 'rsg-components/Styled';
 
-export const styles = ({ space, color, fontFamily, fontSize }) => ({
+export const styles = ({ space, color, fontFamily, fontSize }: Theme) => ({
 	table: {
 		width: '100%',
 		borderCollapse: 'collapse',
@@ -41,7 +41,21 @@ export const styles = ({ space, color, fontFamily, fontSize }) => ({
 	},
 });
 
-export function TableRenderer({ classes, columns, rows, getRowKey }) {
+interface TableProps extends JssInjectedProps {
+	columns: {
+		caption: string;
+		render(row: any): React.ReactNode;
+	}[];
+	rows: any[];
+	getRowKey(row: any): string;
+}
+
+export const TableRenderer: React.FunctionComponent<TableProps> = ({
+	classes,
+	columns,
+	rows,
+	getRowKey,
+}) => {
 	return (
 		<table className={classes.table}>
 			<thead className={classes.tableHead}>
@@ -66,18 +80,18 @@ export function TableRenderer({ classes, columns, rows, getRowKey }) {
 			</tbody>
 		</table>
 	);
-}
+};
 
 TableRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	columns: PropTypes.arrayOf(
 		PropTypes.shape({
 			caption: PropTypes.string.isRequired,
 			render: PropTypes.func.isRequired,
-		})
+		}).isRequired
 	).isRequired,
 	rows: PropTypes.arrayOf(PropTypes.object).isRequired,
 	getRowKey: PropTypes.func.isRequired,
 };
 
-export default Styled(styles)(TableRenderer);
+export default Styled<TableProps>(styles)(TableRenderer);
diff --git a/src/client/rsg-components/Table/__snapshots__/Table.spec.js.snap b/src/client/rsg-components/Table/__snapshots__/Table.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Table/__snapshots__/Table.spec.js.snap
rename to src/client/rsg-components/Table/__snapshots__/Table.spec.tsx.snap
diff --git a/src/client/rsg-components/Table/index.js b/src/client/rsg-components/Table/index.ts
similarity index 100%
rename from src/client/rsg-components/Table/index.js
rename to src/client/rsg-components/Table/index.ts
diff --git a/src/client/rsg-components/TableOfContents/TableOfContents.spec.js b/src/client/rsg-components/TableOfContents/TableOfContents.spec.tsx
similarity index 61%
rename from src/client/rsg-components/TableOfContents/TableOfContents.spec.js
rename to src/client/rsg-components/TableOfContents/TableOfContents.spec.tsx
index c95844f0..7f0c24cc 100644
--- a/src/client/rsg-components/TableOfContents/TableOfContents.spec.js
+++ b/src/client/rsg-components/TableOfContents/TableOfContents.spec.tsx
@@ -1,9 +1,12 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import noop from 'lodash/noop';
+import { SectionViewModel } from 'rsg-components/Section';
+import { ComponentViewModel } from 'rsg-components/ReactComponent';
 import TableOfContents from './TableOfContents';
 import { TableOfContentsRenderer } from './TableOfContentsRenderer';
 
-const components = [
+const components: ComponentViewModel[] = [
 	{
 		name: 'Button',
 		slug: 'button',
@@ -18,7 +21,7 @@ const components = [
 	},
 ];
 
-const sections = [
+const sections: SectionViewModel[] = [
 	{
 		name: 'Introduction',
 		slug: 'introduction',
@@ -51,7 +54,17 @@ const sections = [
 ];
 
 it('should render a renderer', () => {
-	const actual = shallow(<TableOfContents sections={[{ components }]} />);
+	const actual = shallow(
+		<TableOfContents
+			sections={[
+				{
+					name: 'Input',
+					slug: 'input',
+					components,
+				},
+			]}
+		/>
+	);
 
 	expect(actual).toMatchSnapshot();
 });
@@ -64,7 +77,17 @@ it('should render renderer with sections with nested components', () => {
 
 it('should filter list when search field contains a query', () => {
 	const searchTerm = 'but';
-	const actual = shallow(<TableOfContents sections={[{ components }]} />);
+	const actual = shallow(
+		<TableOfContents
+			sections={[
+				{
+					name: 'Input',
+					slug: 'input',
+					components,
+				},
+			]}
+		/>
+	);
 
 	expect(actual).toMatchSnapshot();
 
@@ -87,12 +110,9 @@ it('should filter section names', () => {
 it('renderer should render table of contents', () => {
 	const searchTerm = 'foo';
 	const actual = shallow(
-		<TableOfContentsRenderer
-			classes={{}}
-			items={<div>foo</div>}
-			searchTerm={searchTerm}
-			onSearchTermChange={noop}
-		/>
+		<TableOfContentsRenderer classes={{}} searchTerm={searchTerm} onSearchTermChange={noop}>
+			<div>foo</div>
+		</TableOfContentsRenderer>
 	);
 
 	expect(actual).toMatchSnapshot();
@@ -105,10 +125,11 @@ it('should call a callback when input value changed', () => {
 	const actual = shallow(
 		<TableOfContentsRenderer
 			classes={{}}
-			items={<div>foo</div>}
 			searchTerm={searchTerm}
 			onSearchTermChange={onSearchTermChange}
-		/>
+		>
+			<div>foo</div>
+		</TableOfContentsRenderer>
 	);
 
 	actual.find('input').simulate('change', {
@@ -128,54 +149,54 @@ it('should render content of subsections of a section that has no components', (
 	);
 
 	expect(actual.find('ComponentsList').prop('items')).toMatchInlineSnapshot(`
-Array [
-  Object {
-    "components": Array [],
-    "content": false,
-    "contents": "intro.md",
-    "heading": false,
-    "sections": Array [],
-  },
-  Object {
-    "components": Array [],
-    "content": false,
-    "contents": "chapter.md",
-    "heading": false,
-    "sections": Array [],
-  },
-]
-`);
+		Array [
+		  Object {
+		    "components": Array [],
+		    "content": undefined,
+		    "contents": "intro.md",
+		    "heading": false,
+		    "sections": Array [],
+		  },
+		  Object {
+		    "components": Array [],
+		    "content": undefined,
+		    "contents": "chapter.md",
+		    "heading": false,
+		    "sections": Array [],
+		  },
+		]
+	`);
 });
 
 it('should render components of a single top section as root', () => {
 	const actual = shallow(<TableOfContents sections={[{ components }]} />);
 
 	expect(actual.find('ComponentsList').prop('items')).toMatchInlineSnapshot(`
-Array [
-  Object {
-    "components": Array [],
-    "content": false,
-    "heading": false,
-    "name": "Button",
-    "sections": Array [],
-    "slug": "button",
-  },
-  Object {
-    "components": Array [],
-    "content": false,
-    "heading": false,
-    "name": "Input",
-    "sections": Array [],
-    "slug": "input",
-  },
-  Object {
-    "components": Array [],
-    "content": false,
-    "heading": false,
-    "name": "Textarea",
-    "sections": Array [],
-    "slug": "textarea",
-  },
-]
-`);
+		Array [
+		  Object {
+		    "components": Array [],
+		    "content": undefined,
+		    "heading": false,
+		    "name": "Button",
+		    "sections": Array [],
+		    "slug": "button",
+		  },
+		  Object {
+		    "components": Array [],
+		    "content": undefined,
+		    "heading": false,
+		    "name": "Input",
+		    "sections": Array [],
+		    "slug": "input",
+		  },
+		  Object {
+		    "components": Array [],
+		    "content": undefined,
+		    "heading": false,
+		    "name": "Textarea",
+		    "sections": Array [],
+		    "slug": "textarea",
+		  },
+		]
+	`);
 });
diff --git a/src/client/rsg-components/TableOfContents/TableOfContents.js b/src/client/rsg-components/TableOfContents/TableOfContents.tsx
similarity index 61%
rename from src/client/rsg-components/TableOfContents/TableOfContents.js
rename to src/client/rsg-components/TableOfContents/TableOfContents.tsx
index 91232243..6eef91b0 100644
--- a/src/client/rsg-components/TableOfContents/TableOfContents.js
+++ b/src/client/rsg-components/TableOfContents/TableOfContents.tsx
@@ -2,29 +2,45 @@ import React, { Component } from 'react';
 import PropTypes from 'prop-types';
 import ComponentsList from 'rsg-components/ComponentsList';
 import TableOfContentsRenderer from 'rsg-components/TableOfContents/TableOfContentsRenderer';
+import { SectionViewModel } from 'rsg-components/Section';
+import { ComponentViewModel } from 'rsg-components/ReactComponent';
 import filterSectionsByName from '../../utils/filterSectionsByName';
 
-export default class TableOfContents extends Component {
-	static propTypes = {
+interface TableOfContentsProps {
+	sections: SectionViewModel[];
+	useRouterLinks?: boolean;
+}
+
+export default class TableOfContents extends Component<TableOfContentsProps> {
+	public static propTypes = {
 		sections: PropTypes.array.isRequired,
 		useRouterLinks: PropTypes.bool,
 	};
-	state = {
+	public state = {
 		searchTerm: '',
 	};
 
-	renderLevel(sections, useRouterLinks = false, hashPath = [], useHashId = false) {
-		const items = sections.map(section => {
+	private renderLevel(
+		sections: (SectionViewModel | ComponentViewModel)[],
+		useRouterLinks = false,
+		hashPath: string[] = [],
+		useHashId = false
+	) {
+		const items = sections.map(sectionOrComponent => {
+			const section = sectionOrComponent as SectionViewModel;
 			const children = [...(section.sections || []), ...(section.components || [])];
 			const sectionDepth = section.sectionDepth || 0;
 			const childHashPath =
-				sectionDepth === 0 && useHashId ? hashPath : [...hashPath, section.name];
+				sectionDepth === 0 && useHashId
+					? hashPath
+					: [...hashPath, section.name ? section.name : '-'];
 			return {
 				...section,
 				heading: !!section.name && children.length > 0,
 				content:
-					children.length > 0 &&
-					this.renderLevel(children, useRouterLinks, childHashPath, sectionDepth === 0),
+					children.length > 0
+						? this.renderLevel(children, useRouterLinks, childHashPath, sectionDepth === 0)
+						: undefined,
 			};
 		});
 		return (
@@ -37,7 +53,7 @@ export default class TableOfContents extends Component {
 		);
 	}
 
-	renderSections() {
+	private renderSections() {
 		const { searchTerm } = this.state;
 		const { sections, useRouterLinks } = this.props;
 		// If there is only one section, we treat it as a root section
@@ -52,12 +68,12 @@ export default class TableOfContents extends Component {
 					? sections[0].sections
 					: sections[0].components
 				: sections;
-		const filtered = filterSectionsByName(firstLevel, searchTerm);
+		const filtered = firstLevel ? filterSectionsByName(firstLevel, searchTerm) : firstLevel;
 
-		return this.renderLevel(filtered, useRouterLinks);
+		return filtered ? this.renderLevel(filtered, useRouterLinks) : null;
 	}
 
-	render() {
+	public render() {
 		return (
 			<TableOfContentsRenderer
 				searchTerm={this.state.searchTerm}
diff --git a/src/client/rsg-components/TableOfContents/TableOfContentsRenderer.js b/src/client/rsg-components/TableOfContents/TableOfContentsRenderer.tsx
similarity index 69%
rename from src/client/rsg-components/TableOfContents/TableOfContentsRenderer.js
rename to src/client/rsg-components/TableOfContents/TableOfContentsRenderer.tsx
index 060a75a5..14186fd9 100644
--- a/src/client/rsg-components/TableOfContents/TableOfContentsRenderer.js
+++ b/src/client/rsg-components/TableOfContents/TableOfContentsRenderer.tsx
@@ -1,8 +1,8 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
+import Styled, { Theme, JssInjectedProps } from 'rsg-components/Styled';
 
-const styles = ({ space, color, fontFamily, fontSize, borderRadius }) => ({
+const styles = ({ space, color, fontFamily, fontSize, borderRadius }: Theme) => ({
 	root: {
 		fontFamily: fontFamily.base,
 	},
@@ -35,7 +35,18 @@ const styles = ({ space, color, fontFamily, fontSize, borderRadius }) => ({
 	},
 });
 
-export function TableOfContentsRenderer({ classes, children, searchTerm, onSearchTermChange }) {
+interface TableOfContentsRendererProps extends JssInjectedProps {
+	children?: React.ReactNode;
+	searchTerm: string;
+	onSearchTermChange(term: string): void;
+}
+
+export const TableOfContentsRenderer: React.FunctionComponent<TableOfContentsRendererProps> = ({
+	classes,
+	children,
+	searchTerm,
+	onSearchTermChange,
+}) => {
 	return (
 		<div>
 			<div className={classes.root}>
@@ -54,13 +65,13 @@ export function TableOfContentsRenderer({ classes, children, searchTerm, onSearc
 			</div>
 		</div>
 	);
-}
+};
 
 TableOfContentsRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	children: PropTypes.node,
 	searchTerm: PropTypes.string.isRequired,
 	onSearchTermChange: PropTypes.func.isRequired,
 };
 
-export default Styled(styles)(TableOfContentsRenderer);
+export default Styled<TableOfContentsRendererProps>(styles)(TableOfContentsRenderer);
diff --git a/src/client/rsg-components/TableOfContents/__snapshots__/TableOfContents.spec.js.snap b/src/client/rsg-components/TableOfContents/__snapshots__/TableOfContents.spec.tsx.snap
similarity index 92%
rename from src/client/rsg-components/TableOfContents/__snapshots__/TableOfContents.spec.js.snap
rename to src/client/rsg-components/TableOfContents/__snapshots__/TableOfContents.spec.tsx.snap
index 53f7ddf0..2de95c0e 100644
--- a/src/client/rsg-components/TableOfContents/__snapshots__/TableOfContents.spec.js.snap
+++ b/src/client/rsg-components/TableOfContents/__snapshots__/TableOfContents.spec.tsx.snap
@@ -12,6 +12,9 @@ exports[`renderer should render table of contents 1`] = `
           value="foo"
         />
       </div>
+      <div>
+        foo
+      </div>
     </nav>
   </div>
 </div>
@@ -28,7 +31,7 @@ exports[`should filter list when search field contains a query 1`] = `
       Array [
         Object {
           "components": Array [],
-          "content": false,
+          "content": undefined,
           "heading": false,
           "name": "Button",
           "sections": Array [],
@@ -36,7 +39,7 @@ exports[`should filter list when search field contains a query 1`] = `
         },
         Object {
           "components": Array [],
-          "content": false,
+          "content": undefined,
           "heading": false,
           "name": "Input",
           "sections": Array [],
@@ -44,7 +47,7 @@ exports[`should filter list when search field contains a query 1`] = `
         },
         Object {
           "components": Array [],
-          "content": false,
+          "content": undefined,
           "heading": false,
           "name": "Textarea",
           "sections": Array [],
@@ -69,7 +72,7 @@ exports[`should filter list when search field contains a query 2`] = `
       Array [
         Object {
           "components": Array [],
-          "content": false,
+          "content": undefined,
           "heading": false,
           "name": "Button",
           "sections": Array [],
@@ -94,7 +97,7 @@ exports[`should filter section names 1`] = `
       Array [
         Object {
           "components": Array [],
-          "content": false,
+          "content": undefined,
           "heading": false,
           "name": "Introduction",
           "sections": Array [],
@@ -116,7 +119,7 @@ exports[`should filter section names 1`] = `
             items={
               Array [
                 Object {
-                  "content": false,
+                  "content": undefined,
                   "heading": false,
                   "name": "Button",
                   "slug": "button",
@@ -151,13 +154,13 @@ exports[`should filter section names 1`] = `
             items={
               Array [
                 Object {
-                  "content": false,
+                  "content": undefined,
                   "heading": false,
                   "name": "Input",
                   "slug": "input",
                 },
                 Object {
-                  "content": false,
+                  "content": undefined,
                   "heading": false,
                   "name": "Textarea",
                   "slug": "textarea",
@@ -191,7 +194,7 @@ exports[`should filter section names 2`] = `
       Array [
         Object {
           "components": Array [],
-          "content": false,
+          "content": undefined,
           "heading": false,
           "name": "Forms",
           "sections": Array [],
@@ -216,7 +219,7 @@ exports[`should render a renderer 1`] = `
       Array [
         Object {
           "components": Array [],
-          "content": false,
+          "content": undefined,
           "heading": false,
           "name": "Button",
           "sections": Array [],
@@ -224,7 +227,7 @@ exports[`should render a renderer 1`] = `
         },
         Object {
           "components": Array [],
-          "content": false,
+          "content": undefined,
           "heading": false,
           "name": "Input",
           "sections": Array [],
@@ -232,7 +235,7 @@ exports[`should render a renderer 1`] = `
         },
         Object {
           "components": Array [],
-          "content": false,
+          "content": undefined,
           "heading": false,
           "name": "Textarea",
           "sections": Array [],
@@ -257,7 +260,7 @@ exports[`should render renderer with sections with nested components 1`] = `
       Array [
         Object {
           "components": Array [],
-          "content": false,
+          "content": undefined,
           "heading": false,
           "name": "Introduction",
           "sections": Array [],
@@ -279,7 +282,7 @@ exports[`should render renderer with sections with nested components 1`] = `
             items={
               Array [
                 Object {
-                  "content": false,
+                  "content": undefined,
                   "heading": false,
                   "name": "Button",
                   "slug": "button",
@@ -314,13 +317,13 @@ exports[`should render renderer with sections with nested components 1`] = `
             items={
               Array [
                 Object {
-                  "content": false,
+                  "content": undefined,
                   "heading": false,
                   "name": "Input",
                   "slug": "input",
                 },
                 Object {
-                  "content": false,
+                  "content": undefined,
                   "heading": false,
                   "name": "Textarea",
                   "slug": "textarea",
diff --git a/src/client/rsg-components/TableOfContents/index.js b/src/client/rsg-components/TableOfContents/index.ts
similarity index 100%
rename from src/client/rsg-components/TableOfContents/index.js
rename to src/client/rsg-components/TableOfContents/index.ts
diff --git a/src/client/rsg-components/Text/Text.spec.js b/src/client/rsg-components/Text/Text.spec.tsx
similarity index 93%
rename from src/client/rsg-components/Text/Text.spec.js
rename to src/client/rsg-components/Text/Text.spec.tsx
index e4293dab..24678cf3 100644
--- a/src/client/rsg-components/Text/Text.spec.js
+++ b/src/client/rsg-components/Text/Text.spec.tsx
@@ -1,5 +1,7 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import { TextRenderer, styles } from './TextRenderer';
+import classes from '../../../../test/classes';
 
 const props = {
 	classes: classes(styles),
diff --git a/src/client/rsg-components/Text/TextRenderer.js b/src/client/rsg-components/Text/TextRenderer.tsx
similarity index 62%
rename from src/client/rsg-components/Text/TextRenderer.js
rename to src/client/rsg-components/Text/TextRenderer.tsx
index 739be0bb..354e4a7d 100644
--- a/src/client/rsg-components/Text/TextRenderer.js
+++ b/src/client/rsg-components/Text/TextRenderer.tsx
@@ -1,9 +1,9 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
 import cx from 'clsx';
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
 
-export const styles = ({ fontFamily, fontSize, color }) => ({
+export const styles = ({ fontFamily, fontSize, color }: Theme) => ({
 	text: {
 		fontFamily: fontFamily.base,
 	},
@@ -36,10 +36,27 @@ export const styles = ({ fontFamily, fontSize, color }) => ({
 	},
 });
 
-export function TextRenderer({ classes, semantic, size, color, underlined, children, ...props }) {
+interface TextProps extends JssInjectedProps {
+	semantic?: 'em' | 'strong';
+	size?: 'inherit' | 'small' | 'base' | 'text';
+	color?: 'base' | 'light';
+	underlined?: boolean;
+	children: React.ReactNode;
+	[intrinsicAttribute: string]: any;
+}
+
+export const TextRenderer: React.FunctionComponent<TextProps> = ({
+	classes,
+	semantic,
+	size,
+	color,
+	underlined,
+	children,
+	...props
+}) => {
 	const Tag = semantic || 'span';
 	const classNames = cx(classes.text, classes[`${size}Size`], classes[`${color}Color`], {
-		[classes[semantic]]: semantic,
+		[classes[Tag]]: !!semantic,
 		[classes.isUnderlined]: underlined,
 	});
 
@@ -48,10 +65,10 @@ export function TextRenderer({ classes, semantic, size, color, underlined, child
 			{children}
 		</Tag>
 	);
-}
+};
 
 TextRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	semantic: PropTypes.oneOf(['em', 'strong']),
 	size: PropTypes.oneOf(['inherit', 'small', 'base', 'text']),
 	color: PropTypes.oneOf(['base', 'light']),
@@ -65,4 +82,4 @@ TextRenderer.defaultProps = {
 	underlined: false,
 };
 
-export default Styled(styles)(TextRenderer);
+export default Styled<TextProps>(styles)(TextRenderer);
diff --git a/src/client/rsg-components/Text/__snapshots__/Text.spec.js.snap b/src/client/rsg-components/Text/__snapshots__/Text.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Text/__snapshots__/Text.spec.js.snap
rename to src/client/rsg-components/Text/__snapshots__/Text.spec.tsx.snap
diff --git a/src/client/rsg-components/Text/index.js b/src/client/rsg-components/Text/index.ts
similarity index 100%
rename from src/client/rsg-components/Text/index.js
rename to src/client/rsg-components/Text/index.ts
diff --git a/src/client/rsg-components/Type/Type.spec.js b/src/client/rsg-components/Type/Type.spec.tsx
similarity index 77%
rename from src/client/rsg-components/Type/Type.spec.js
rename to src/client/rsg-components/Type/Type.spec.tsx
index 6bcb6e0f..c2b6f4f5 100644
--- a/src/client/rsg-components/Type/Type.spec.js
+++ b/src/client/rsg-components/Type/Type.spec.tsx
@@ -1,5 +1,7 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import { TypeRenderer, styles } from './TypeRenderer';
+import classes from '../../../../test/classes';
 
 const props = {
 	classes: classes(styles),
diff --git a/src/client/rsg-components/Type/TypeRenderer.js b/src/client/rsg-components/Type/TypeRenderer.js
deleted file mode 100644
index 57781155..00000000
--- a/src/client/rsg-components/Type/TypeRenderer.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
-
-export const styles = ({ fontFamily, fontSize, color }) => ({
-	type: {
-		fontFamily: fontFamily.monospace,
-		fontSize: fontSize.small,
-		color: color.type,
-	},
-});
-
-export function TypeRenderer({ classes, children }) {
-	return <span className={classes.type}>{children}</span>;
-}
-
-TypeRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
-	children: PropTypes.node.isRequired,
-};
-
-export default Styled(styles)(TypeRenderer);
diff --git a/src/client/rsg-components/Type/TypeRenderer.tsx b/src/client/rsg-components/Type/TypeRenderer.tsx
new file mode 100644
index 00000000..f08a1440
--- /dev/null
+++ b/src/client/rsg-components/Type/TypeRenderer.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
+
+export const styles = ({ fontFamily, fontSize, color }: Theme) => ({
+	type: {
+		fontFamily: fontFamily.monospace,
+		fontSize: fontSize.small,
+		color: color.type,
+	},
+});
+
+interface TypeProps extends JssInjectedProps {
+	children: React.ReactNode;
+}
+
+export const TypeRenderer: React.FunctionComponent<TypeProps> = ({ classes, children }) => {
+	return <span className={classes.type}>{children}</span>;
+};
+
+TypeRenderer.propTypes = {
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
+	children: PropTypes.node.isRequired,
+};
+
+export default Styled<TypeProps>(styles)(TypeRenderer);
diff --git a/src/client/rsg-components/Type/__snapshots__/Type.spec.js.snap b/src/client/rsg-components/Type/__snapshots__/Type.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Type/__snapshots__/Type.spec.js.snap
rename to src/client/rsg-components/Type/__snapshots__/Type.spec.tsx.snap
diff --git a/src/client/rsg-components/Type/index.js b/src/client/rsg-components/Type/index.ts
similarity index 100%
rename from src/client/rsg-components/Type/index.js
rename to src/client/rsg-components/Type/index.ts
diff --git a/src/client/rsg-components/Usage/Usage.js b/src/client/rsg-components/Usage/Usage.js
deleted file mode 100644
index 83448a4b..00000000
--- a/src/client/rsg-components/Usage/Usage.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Props from 'rsg-components/Props';
-import Methods from 'rsg-components/Methods';
-import isEmpty from 'lodash/isEmpty';
-
-export default function Usage({ props: { props, methods } }) {
-	const propsNode = !isEmpty(props) && <Props props={props} />;
-	const methodsNode = !isEmpty(methods) && <Methods methods={methods} />;
-
-	if (!propsNode && !methodsNode) {
-		return null;
-	}
-
-	return (
-		<div>
-			{propsNode}
-			{methodsNode}
-		</div>
-	);
-}
-
-Usage.propTypes = {
-	props: PropTypes.shape({
-		props: PropTypes.array,
-		methods: PropTypes.array,
-	}).isRequired,
-};
diff --git a/src/client/rsg-components/Usage/Usage.spec.js b/src/client/rsg-components/Usage/Usage.spec.tsx
similarity index 81%
rename from src/client/rsg-components/Usage/Usage.spec.js
rename to src/client/rsg-components/Usage/Usage.spec.tsx
index ee987ea5..b230bc3b 100644
--- a/src/client/rsg-components/Usage/Usage.spec.js
+++ b/src/client/rsg-components/Usage/Usage.spec.tsx
@@ -1,15 +1,17 @@
 import React from 'react';
+import { shallow } from 'enzyme';
+import { PropTypeDescriptor, MethodDescriptor } from 'react-docgen';
 import Usage from './Usage';
 
 const props = [
 	{
 		name: 'children',
-		type: { name: 'string' },
+		type: { name: 'string' } as PropTypeDescriptor,
 		required: true,
 		description: 'Button label.',
 	},
 ];
-const methods = [
+const methods: MethodDescriptor[] = [
 	{
 		name: 'set',
 		params: [
diff --git a/src/client/rsg-components/Usage/Usage.tsx b/src/client/rsg-components/Usage/Usage.tsx
new file mode 100644
index 00000000..93825b5e
--- /dev/null
+++ b/src/client/rsg-components/Usage/Usage.tsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { MethodDescriptor } from 'react-docgen';
+import { PropDescriptorWithFlow } from 'rsg-components/Props/util';
+import Props from 'rsg-components/Props';
+import Methods from 'rsg-components/Methods';
+import isEmpty from 'lodash/isEmpty';
+
+const Usage: React.FunctionComponent<{
+	props: { methods?: MethodDescriptor[]; props?: PropDescriptorWithFlow[] };
+}> = ({ props: { props, methods } }) => {
+	const propsNode = props && !isEmpty(props) && <Props props={props} />;
+	const methodsNode = methods && !isEmpty(methods) && <Methods methods={methods} />;
+
+	if (!propsNode && !methodsNode) {
+		return null;
+	}
+
+	return (
+		<div>
+			{propsNode}
+			{methodsNode}
+		</div>
+	);
+};
+
+Usage.propTypes = {
+	props: PropTypes.shape({
+		props: PropTypes.array,
+		methods: PropTypes.array,
+	}).isRequired,
+};
+
+export default Usage;
diff --git a/src/client/rsg-components/Usage/__snapshots__/Usage.spec.js.snap b/src/client/rsg-components/Usage/__snapshots__/Usage.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Usage/__snapshots__/Usage.spec.js.snap
rename to src/client/rsg-components/Usage/__snapshots__/Usage.spec.tsx.snap
diff --git a/src/client/rsg-components/Usage/index.js b/src/client/rsg-components/Usage/index.ts
similarity index 100%
rename from src/client/rsg-components/Usage/index.js
rename to src/client/rsg-components/Usage/index.ts
diff --git a/src/client/rsg-components/Version/Version.spec.js b/src/client/rsg-components/Version/Version.spec.tsx
similarity index 87%
rename from src/client/rsg-components/Version/Version.spec.js
rename to src/client/rsg-components/Version/Version.spec.tsx
index e164219c..974f300c 100644
--- a/src/client/rsg-components/Version/Version.spec.js
+++ b/src/client/rsg-components/Version/Version.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { render } from 'enzyme';
 import VersionRenderer from './VersionRenderer';
 
 it('renderer should render version', () => {
diff --git a/src/client/rsg-components/Version/VersionRenderer.js b/src/client/rsg-components/Version/VersionRenderer.js
deleted file mode 100644
index 65a83144..00000000
--- a/src/client/rsg-components/Version/VersionRenderer.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Styled from 'rsg-components/Styled';
-
-const styles = ({ color, fontFamily, fontSize }) => ({
-	version: {
-		color: color.light,
-		margin: [[5, 0, 0, 0]],
-		fontFamily: fontFamily.base,
-		fontSize: fontSize.base,
-		fontWeight: 'normal',
-	},
-});
-
-export function VersionRenderer({ classes, children }) {
-	return (
-		<p aria-label="version" className={classes.version}>
-			{children}
-		</p>
-	);
-}
-
-VersionRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
-	children: PropTypes.node,
-};
-
-export default Styled(styles)(VersionRenderer);
diff --git a/src/client/rsg-components/Version/VersionRenderer.tsx b/src/client/rsg-components/Version/VersionRenderer.tsx
new file mode 100644
index 00000000..954c81ad
--- /dev/null
+++ b/src/client/rsg-components/Version/VersionRenderer.tsx
@@ -0,0 +1,32 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
+
+const styles = ({ color, fontFamily, fontSize }: Theme) => ({
+	version: {
+		color: color.light,
+		margin: [[5, 0, 0, 0]],
+		fontFamily: fontFamily.base,
+		fontSize: fontSize.base,
+		fontWeight: 'normal',
+	},
+});
+
+interface VersionProps extends JssInjectedProps {
+	children?: React.ReactNode;
+}
+
+export const VersionRenderer: React.FunctionComponent<VersionProps> = ({ classes, children }) => {
+	return (
+		<p aria-label="version" className={classes.version}>
+			{children}
+		</p>
+	);
+};
+
+VersionRenderer.propTypes = {
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
+	children: PropTypes.node,
+};
+
+export default Styled<VersionProps>(styles)(VersionRenderer);
diff --git a/src/client/rsg-components/Version/__snapshots__/Version.spec.js.snap b/src/client/rsg-components/Version/__snapshots__/Version.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Version/__snapshots__/Version.spec.js.snap
rename to src/client/rsg-components/Version/__snapshots__/Version.spec.tsx.snap
diff --git a/src/client/rsg-components/Version/index.js b/src/client/rsg-components/Version/index.ts
similarity index 100%
rename from src/client/rsg-components/Version/index.js
rename to src/client/rsg-components/Version/index.ts
diff --git a/src/client/rsg-components/Welcome/Welcome.spec.js b/src/client/rsg-components/Welcome/Welcome.spec.tsx
similarity index 88%
rename from src/client/rsg-components/Welcome/Welcome.spec.js
rename to src/client/rsg-components/Welcome/Welcome.spec.tsx
index 40c0569a..fc2b8848 100644
--- a/src/client/rsg-components/Welcome/Welcome.spec.js
+++ b/src/client/rsg-components/Welcome/Welcome.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import { WelcomeRenderer } from './WelcomeRenderer';
 
 it('renderer should render welcome screen', () => {
diff --git a/src/client/rsg-components/Welcome/WelcomeRenderer.js b/src/client/rsg-components/Welcome/WelcomeRenderer.tsx
similarity index 64%
rename from src/client/rsg-components/Welcome/WelcomeRenderer.js
rename to src/client/rsg-components/Welcome/WelcomeRenderer.tsx
index 8643943b..9c869e96 100644
--- a/src/client/rsg-components/Welcome/WelcomeRenderer.js
+++ b/src/client/rsg-components/Welcome/WelcomeRenderer.tsx
@@ -1,10 +1,10 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import Markdown from 'rsg-components/Markdown';
-import Styled from 'rsg-components/Styled';
+import Styled, { JssInjectedProps, Theme } from 'rsg-components/Styled';
 import { DOCS_COMPONENTS } from '../../../scripts/consts';
 
-const styles = ({ space, maxWidth }) => ({
+const styles = ({ space, maxWidth }: Theme) => ({
 	root: {
 		maxWidth,
 		margin: [[0, 'auto']],
@@ -12,7 +12,11 @@ const styles = ({ space, maxWidth }) => ({
 	},
 });
 
-export function WelcomeRenderer({ classes, patterns }) {
+interface WelcomeProps extends JssInjectedProps {
+	patterns: string[];
+}
+
+export const WelcomeRenderer: React.FunctionComponent<WelcomeProps> = ({ classes, patterns }) => {
 	return (
 		<div className={classes.root}>
 			<Markdown
@@ -34,11 +38,11 @@ Read more in the [locating components guide](${DOCS_COMPONENTS}).
 			/>
 		</div>
 	);
-}
+};
 
 WelcomeRenderer.propTypes = {
-	classes: PropTypes.object.isRequired,
+	classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
 	patterns: PropTypes.array.isRequired,
 };
 
-export default Styled(styles)(WelcomeRenderer);
+export default Styled<WelcomeProps>(styles)(WelcomeRenderer);
diff --git a/src/client/rsg-components/Welcome/__snapshots__/Welcome.spec.js.snap b/src/client/rsg-components/Welcome/__snapshots__/Welcome.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Welcome/__snapshots__/Welcome.spec.js.snap
rename to src/client/rsg-components/Welcome/__snapshots__/Welcome.spec.tsx.snap
diff --git a/src/client/rsg-components/Welcome/index.js b/src/client/rsg-components/Welcome/index.ts
similarity index 100%
rename from src/client/rsg-components/Welcome/index.js
rename to src/client/rsg-components/Welcome/index.ts
diff --git a/src/client/rsg-components/Wrapper/Wrapper.spec.js b/src/client/rsg-components/Wrapper/Wrapper.spec.tsx
similarity index 67%
rename from src/client/rsg-components/Wrapper/Wrapper.spec.js
rename to src/client/rsg-components/Wrapper/Wrapper.spec.tsx
index 5e32b774..ba506938 100644
--- a/src/client/rsg-components/Wrapper/Wrapper.spec.js
+++ b/src/client/rsg-components/Wrapper/Wrapper.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import Wrapper from './Wrapper';
 
 it('should render children', () => {
@@ -13,8 +14,12 @@ it('should call onError handler when React invokes error handler', () => {
 	const actual = shallow(<Wrapper onError={onError}>blah</Wrapper>);
 
 	// faux error
-	actual.instance().componentDidCatch('err');
+	const err = new Error('err');
+	const inst = actual.instance();
+	if (inst && inst.componentDidCatch) {
+		inst.componentDidCatch(err, { componentStack: '' });
+	}
 
 	expect(onError).toHaveBeenCalledTimes(1);
-	expect(onError).toHaveBeenCalledWith('err');
+	expect(onError).toHaveBeenCalledWith(err);
 });
diff --git a/src/client/rsg-components/Wrapper/Wrapper.js b/src/client/rsg-components/Wrapper/Wrapper.ts
similarity index 56%
rename from src/client/rsg-components/Wrapper/Wrapper.js
rename to src/client/rsg-components/Wrapper/Wrapper.ts
index f35a564f..70c5b90a 100644
--- a/src/client/rsg-components/Wrapper/Wrapper.js
+++ b/src/client/rsg-components/Wrapper/Wrapper.ts
@@ -1,17 +1,17 @@
 import { Component } from 'react';
 import PropTypes from 'prop-types';
 
-export default class Wrapper extends Component {
-	static propTypes = {
+export default class Wrapper extends Component<{ onError: (e: Error) => void }> {
+	public static propTypes = {
 		children: PropTypes.node.isRequired,
 		onError: PropTypes.func.isRequired,
 	};
 
-	componentDidCatch(error) {
+	public componentDidCatch(error: Error) {
 		this.props.onError(error);
 	}
 
-	render() {
+	public render() {
 		return this.props.children;
 	}
 }
diff --git a/src/client/rsg-components/Wrapper/__snapshots__/Wrapper.spec.js.snap b/src/client/rsg-components/Wrapper/__snapshots__/Wrapper.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/Wrapper/__snapshots__/Wrapper.spec.js.snap
rename to src/client/rsg-components/Wrapper/__snapshots__/Wrapper.spec.tsx.snap
diff --git a/src/client/rsg-components/Wrapper/index.js b/src/client/rsg-components/Wrapper/index.ts
similarity index 100%
rename from src/client/rsg-components/Wrapper/index.js
rename to src/client/rsg-components/Wrapper/index.ts
diff --git a/src/client/rsg-components/slots/CodeTabButton.js b/src/client/rsg-components/slots/CodeTabButton.tsx
similarity index 76%
rename from src/client/rsg-components/slots/CodeTabButton.js
rename to src/client/rsg-components/slots/CodeTabButton.tsx
index 4f05c4ef..7981912c 100644
--- a/src/client/rsg-components/slots/CodeTabButton.js
+++ b/src/client/rsg-components/slots/CodeTabButton.tsx
@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import TabButton from 'rsg-components/TabButton';
 
-const CodeTabButton = props => <TabButton {...props}>View Code</TabButton>;
+const CodeTabButton = (props: any) => <TabButton {...props}>View Code</TabButton>;
 
 CodeTabButton.propTypes = {
 	onClick: PropTypes.func.isRequired,
diff --git a/src/client/rsg-components/slots/IsolateButton.spec.js b/src/client/rsg-components/slots/IsolateButton.spec.tsx
similarity index 94%
rename from src/client/rsg-components/slots/IsolateButton.spec.js
rename to src/client/rsg-components/slots/IsolateButton.spec.tsx
index db22ab11..02b6b914 100644
--- a/src/client/rsg-components/slots/IsolateButton.spec.js
+++ b/src/client/rsg-components/slots/IsolateButton.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import IsolateButton from './IsolateButton';
 
 it('should renderer a link to isolated mode', () => {
diff --git a/src/client/rsg-components/slots/IsolateButton.js b/src/client/rsg-components/slots/IsolateButton.tsx
similarity index 83%
rename from src/client/rsg-components/slots/IsolateButton.js
rename to src/client/rsg-components/slots/IsolateButton.tsx
index 92e4ea34..86f01667 100644
--- a/src/client/rsg-components/slots/IsolateButton.js
+++ b/src/client/rsg-components/slots/IsolateButton.tsx
@@ -4,7 +4,13 @@ import { MdFullscreen, MdFullscreenExit } from 'react-icons/md';
 import ToolbarButton from 'rsg-components/ToolbarButton';
 import getUrl from '../../utils/getUrl';
 
-const IsolateButton = ({ name, example, isolated }) => {
+interface IsolateButtonProps {
+	name: string;
+	example?: number;
+	isolated?: boolean;
+}
+
+const IsolateButton = ({ name, example, isolated }: IsolateButtonProps) => {
 	const testID = example ? `${name}-${example}-isolate-button` : `${name}-isolate-button`;
 
 	return isolated ? (
diff --git a/src/client/rsg-components/slots/UsageTabButton.spec.js b/src/client/rsg-components/slots/UsageTabButton.spec.tsx
similarity index 93%
rename from src/client/rsg-components/slots/UsageTabButton.spec.js
rename to src/client/rsg-components/slots/UsageTabButton.spec.tsx
index a1b0edfc..d4b0adac 100644
--- a/src/client/rsg-components/slots/UsageTabButton.spec.js
+++ b/src/client/rsg-components/slots/UsageTabButton.spec.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { shallow } from 'enzyme';
 import UsageTabButton from './UsageTabButton';
 
 const props = {
diff --git a/src/client/rsg-components/slots/UsageTabButton.js b/src/client/rsg-components/slots/UsageTabButton.tsx
similarity index 73%
rename from src/client/rsg-components/slots/UsageTabButton.js
rename to src/client/rsg-components/slots/UsageTabButton.tsx
index 227438a2..22af1036 100644
--- a/src/client/rsg-components/slots/UsageTabButton.js
+++ b/src/client/rsg-components/slots/UsageTabButton.tsx
@@ -3,7 +3,17 @@ import PropTypes from 'prop-types';
 import TabButton from 'rsg-components/TabButton';
 import isEmpty from 'lodash/isEmpty';
 
-const UsageTabButton = props => {
+interface UsageTabButtonProps {
+	name: string;
+	onClick: (e: React.MouseEvent) => void;
+	active?: boolean;
+	props: {
+		props?: any[];
+		methods?: any[];
+	};
+}
+
+const UsageTabButton = (props: UsageTabButtonProps) => {
 	const component = props.props;
 	const showButton = !isEmpty(component.props) || !isEmpty(component.methods);
 	return showButton ? <TabButton {...props}>Props & methods</TabButton> : null;
diff --git a/src/client/utils/__tests__/getFilterRegExp.spec.js b/src/client/utils/__tests__/getFilterRegExp.spec.ts
similarity index 100%
rename from src/client/utils/__tests__/getFilterRegExp.spec.js
rename to src/client/utils/__tests__/getFilterRegExp.spec.ts
diff --git a/src/client/utils/__tests__/getUrl.spec.js b/src/client/utils/__tests__/getUrl.spec.ts
similarity index 100%
rename from src/client/utils/__tests__/getUrl.spec.js
rename to src/client/utils/__tests__/getUrl.spec.ts
diff --git a/src/client/utils/__tests__/handleHash.spec.js b/src/client/utils/__tests__/handleHash.spec.ts
similarity index 100%
rename from src/client/utils/__tests__/handleHash.spec.js
rename to src/client/utils/__tests__/handleHash.spec.ts
diff --git a/src/client/utils/filterSectionsByName.js b/src/client/utils/filterSectionsByName.js
deleted file mode 100644
index fa041f67..00000000
--- a/src/client/utils/filterSectionsByName.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import getFilterRegExp from './getFilterRegExp';
-import filterComponentsByName from './filterComponentsByName';
-
-/**
- * Fuzzy filters sections by section or component name.
- *
- * @param {Array} sections
- * @param {string} query
- * @return {Array}
- */
-export default function filterSectionsByName(sections, query) {
-	const regExp = getFilterRegExp(query);
-
-	return sections
-		.map(section => ({
-			...section,
-			sections: section.sections ? filterSectionsByName(section.sections, query) : [],
-			components: section.components ? filterComponentsByName(section.components, query) : [],
-		}))
-		.filter(
-			section =>
-				section.components.length > 0 || section.sections.length > 0 || regExp.test(section.name)
-		);
-}
diff --git a/src/client/utils/filterSectionsByName.ts b/src/client/utils/filterSectionsByName.ts
new file mode 100644
index 00000000..fb5d1db8
--- /dev/null
+++ b/src/client/utils/filterSectionsByName.ts
@@ -0,0 +1,34 @@
+import { SectionViewModel } from 'rsg-components/Section';
+import { ComponentViewModel } from 'rsg-components/ReactComponent';
+import getFilterRegExp from './getFilterRegExp';
+import filterComponentsByName from './filterComponentsByName';
+
+/**
+ * Fuzzy filters sections by section or component name.
+ *
+ * @param {Array} sections
+ * @param {string} query
+ * @return {Array}
+ */
+export default function filterSectionsByName(
+	sections: (SectionViewModel | ComponentViewModel)[],
+	query: string
+): SectionViewModel[] {
+	const regExp = getFilterRegExp(query);
+
+	return sections
+		.map(sectionOrComponent => {
+			const section = sectionOrComponent as SectionViewModel;
+			return {
+				...section,
+				sections: section.sections ? filterSectionsByName(section.sections, query) : [],
+				components: section.components ? filterComponentsByName(section.components, query) : [],
+			};
+		})
+		.filter(
+			section =>
+				section.components.length > 0 ||
+				section.sections.length > 0 ||
+				regExp.test(section.name || '-')
+		);
+}
diff --git a/src/client/utils/getFilterRegExp.js b/src/client/utils/getFilterRegExp.ts
similarity index 77%
rename from src/client/utils/getFilterRegExp.js
rename to src/client/utils/getFilterRegExp.ts
index da668ee1..480b6dfb 100644
--- a/src/client/utils/getFilterRegExp.js
+++ b/src/client/utils/getFilterRegExp.ts
@@ -4,7 +4,7 @@
  * @param {string} query
  * @return {RegExp}
  */
-export default function getFilterRegExp(query) {
+export default function getFilterRegExp(query: string): RegExp {
 	query = query
 		.replace(/[^a-z0-9]/gi, '')
 		.split('')
diff --git a/src/client/utils/getUrl.js b/src/client/utils/getUrl.ts
similarity index 58%
rename from src/client/utils/getUrl.js
rename to src/client/utils/getUrl.ts
index 63b27389..076a4b78 100644
--- a/src/client/utils/getUrl.js
+++ b/src/client/utils/getUrl.ts
@@ -1,20 +1,82 @@
+/**
+ * Gets the URL fragment for an isolated or nochrome link.
+ *
+ * @param {string} $.currentHash The current hash fragment of the page
+ * @param {string} $.encodedName The URL encoded name of the component
+ * @return {string}
+ */
+function buildIsolatedOrNoChromeFragment({
+	currentHash,
+	encodedName,
+}: {
+	currentHash: string;
+	encodedName: string;
+}): string {
+	const stripFragment = /^#\/?/;
+	const stripTrailingSlash = /\/$/;
+	const currentHashPath =
+		// skip if we are already using `#!/`
+		currentHash && !currentHash.includes('#!/')
+			? currentHash.replace(stripFragment, '').replace(stripTrailingSlash, '') + '/'
+			: '';
+	return `#!/${currentHashPath}${encodedName}`;
+}
+
+interface GetUrlOptions {
+	name: string;
+	slug: string;
+	/**
+	 * Example index
+	 */
+	example: number;
+	anchor: boolean;
+	/**
+	 * Isolated mode
+	 */
+	isolated: boolean;
+	/**
+	 * No chrome? (Can be combined with anchor or isolated)
+	 */
+	nochrome: boolean;
+	/**
+	 * Absolute URL? (Can be combined with other flags)
+	 */
+	absolute: boolean;
+	hashPath: string[] | false;
+	id: boolean;
+	takeHash: boolean;
+}
+
 /**
  * Get component / section URL.
  *
- * @param {string} $.name Name
- * @param {string} $.slug Slug
- * @param {number} $.example Example index
- * @param {boolean} $.anchor Anchor?
- * @param {boolean} $.isolated Isolated mode?
- * @param {boolean} $.nochrome No chrome? (Can be combined with anchor or isolated)
- * @param {boolean} $.absolute Absolute URL? (Can be combined with other flags)
- * @param {object} [location] Location object (will use current page location by default)
+ * @param {GetUrlOptions} options
+ * @param location Location object (will use current page location by default)
  * @return {string}
  */
 export default function getUrl(
-	{ name, slug, example, anchor, isolated, nochrome, absolute, hashPath, id, takeHash } = {},
-	{ origin, pathname, hash = '' } = window.location
-) {
+	{
+		name,
+		slug,
+		example,
+		anchor,
+		isolated,
+		nochrome,
+		absolute,
+		hashPath,
+		id,
+		takeHash,
+	}: Partial<GetUrlOptions> = {},
+	{
+		origin,
+		pathname,
+		hash = '',
+	}: {
+		origin: string;
+		pathname: string;
+		hash: string;
+	} = window.location
+): string {
 	let url = pathname;
 
 	const currentHash = hash.indexOf('?') > -1 ? hash.substring(0, hash.indexOf('?')) : hash;
@@ -27,7 +89,7 @@ export default function getUrl(
 		url += '?nochrome';
 	}
 
-	const encodedName = encodeURIComponent(name);
+	const encodedName = encodeURIComponent(name || '');
 
 	if (anchor) {
 		url += `#${slug}`;
@@ -57,21 +119,3 @@ export default function getUrl(
 
 	return url;
 }
-
-/**
- * Gets the URL fragment for an isolated or nochrome link.
- *
- * @param {string} $.currentHash The current hash fragment of the page
- * @param {string} $.encodedName The URL encoded name of the component
- * @return {string}
- */
-function buildIsolatedOrNoChromeFragment({ currentHash, encodedName }) {
-	const stripFragment = /^#\/?/;
-	const stripTrailingSlash = /\/$/;
-	const currentHashPath =
-		// skip if we are already using `#!/`
-		currentHash && !currentHash.includes('#!/')
-			? currentHash.replace(stripFragment, '').replace(stripTrailingSlash, '') + '/'
-			: '';
-	return `#!/${currentHashPath}${encodedName}`;
-}
diff --git a/src/client/utils/handleHash.js b/src/client/utils/handleHash.ts
similarity index 80%
rename from src/client/utils/handleHash.js
rename to src/client/utils/handleHash.ts
index b7b9d1f5..d8ddb86b 100644
--- a/src/client/utils/handleHash.js
+++ b/src/client/utils/handleHash.ts
@@ -8,7 +8,7 @@ const defaultPrependHash = '#/';
 const separator = '/';
 const hashValRegexp = /(.*)\?/;
 
-function trimHash(hash, prependHash) {
+function trimHash(hash: string, prependHash?: string): string {
 	if (!hash) {
 		return '';
 	}
@@ -16,7 +16,7 @@ function trimHash(hash, prependHash) {
 	return hash.replace(regexp, '');
 }
 
-const trimParams = hash => {
+const trimParams = (hash: string): string => {
 	const result = hashValRegexp.exec(hash);
 	return (result && result[1]) || hash;
 };
@@ -28,7 +28,7 @@ const trimParams = hash => {
  * @param {string} search
  * @return {boolean}
  */
-export const hasInHash = (hash, search) => {
+export const hasInHash = (hash: string, search: string): boolean => {
 	return hash !== '' && hash.indexOf(search) > -1;
 };
 
@@ -39,7 +39,7 @@ export const hasInHash = (hash, search) => {
  * @param {string} prependHash
  * @return {string}
  */
-export const getHash = (hash, prependHash) => {
+export const getHash = (hash: string, prependHash?: string) => {
 	return decodeURIComponent(trimParams(trimHash(hash, prependHash)));
 };
 
@@ -50,7 +50,7 @@ export const getHash = (hash, prependHash) => {
  * @param {string} prependHash
  * @return {Array.<string>}
  */
-export const getHashAsArray = (hash, prependHash) => {
+export const getHashAsArray = (hash: string, prependHash?: string): string[] => {
 	return trimParams(trimHash(hash, prependHash))
 		.split(separator)
 		.map(decodeURIComponent);
@@ -63,7 +63,7 @@ export const getHashAsArray = (hash, prependHash) => {
  * @param {string} name
  * @return {string}
  */
-export const getParameterByName = (hash, name) => {
+export const getParameterByName = (hash: string, name: string): string | null => {
 	name = name.replace(/[[\]]/g, '\\$&');
 	const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
 	const results = regex.exec(hash);
diff --git a/src/scripts/schemas/config.ts b/src/scripts/schemas/config.ts
index c0a5915b..c60721f5 100644
--- a/src/scripts/schemas/config.ts
+++ b/src/scripts/schemas/config.ts
@@ -33,19 +33,23 @@ const DEFAULT_COMPONENTS_PATTERN =
 
 const logger = glogg('rsg');
 
-export type EXPAND_MODE = 'expand' | 'collapse';
+export type EXPAND_MODE = 'expand' | 'collapse' | 'hide';
 
 export interface ProcessedStyleguidistConfig {
 	compilerConfig: TransformOptions;
 	showCode: EXPAND_MODE;
 	showUsage: EXPAND_MODE;
 	components: string;
-	theme: RecursivePartial<Theme>;
-	// TODO: be more specific about this typing
+	theme: Theme;
 	styles: Styles;
+	pagePerSection: boolean;
+	ribbon?: {
+		text?: string;
+		url: string;
+	};
 }
 
-export type StyleguidistConfig = Partial<ProcessedStyleguidistConfig>;
+export type StyleguidistConfig = RecursivePartial<ProcessedStyleguidistConfig>;
 
 const configSchema = {
 	assetsDir: {
diff --git a/src/typings/react-docgen.d.ts b/src/typings/react-docgen.d.ts
index 2db23e3c..e4c78a04 100644
--- a/src/typings/react-docgen.d.ts
+++ b/src/typings/react-docgen.d.ts
@@ -14,7 +14,34 @@ declare module 'react-docgen' {
 		toObject(): DocumentationObject;
 	}
 
-	interface PropTypeDescriptor {
+	export interface TagObject {
+		title: string;
+		description?: string;
+	}
+
+	export interface TagParamObject {
+		title: string;
+		description?: string;
+		name: string;
+		type?: any;
+		default?: string;
+	}
+
+	export interface TagProps {
+		deprecated?: TagObject[];
+		see?: TagObject[];
+		link?: TagObject[];
+		author?: TagObject[];
+		version?: TagObject[];
+		since?: TagObject[];
+		returns?: TagObject[];
+		return?: TagParamObject[];
+		arg?: TagParamObject[];
+		argument?: TagParamObject[];
+		param?: TagParamObject[];
+	}
+
+	export interface PropTypeDescriptor {
 		name:
 			| 'arrayOf'
 			| 'custom'
@@ -33,6 +60,7 @@ declare module 'react-docgen' {
 			| 'shape'
 			| 'exact'
 			| 'union'
+			| 'instanceOf'
 			| 'elementType';
 		value?: any;
 		raw?: string;
@@ -43,29 +71,41 @@ declare module 'react-docgen' {
 		required?: boolean;
 	}
 
-	interface PropDescriptor {
+	export interface PropDescriptor {
+		name: string;
 		type?: PropTypeDescriptor;
 		required?: boolean;
 		defaultValue?: any;
 		description?: string;
+		tags?: TagProps;
+	}
+
+	export interface MethodDescriptor {
+		name: string;
+		description?: string;
+		returns?: { name: string; [key: string]: any } | null;
+		params?: any[];
+		modifiers?: string[];
+		tags?: TagProps;
 	}
 
-	interface DocumentationObject {
+	export interface DocumentationObject {
 		props?: { [propName: string]: PropDescriptor };
+		methods?: MethodDescriptor[];
 		context?: { [constextName: string]: PropDescriptor };
 		childContext?: { [chilCOntextName: string]: PropDescriptor };
 		composes?: string[];
 	}
 
 	interface Options {
-		filename: string;
-		cwd: string;
-		babelrc: string;
-		babelrcRoots: boolean | string | string[];
-		root: string;
-		rootMode: 'root' | 'upward' | 'upward-optional';
-		configFile: string;
-		envName: string;
+		filename?: string;
+		cwd?: string;
+		babelrc?: string;
+		babelrcRoots?: boolean | string | string[];
+		root?: string;
+		rootMode?: 'root' | 'upward' | 'upward-optional';
+		configFile?: string;
+		envName?: string;
 	}
 
 	export const defaultHandlers: Handler[];
diff --git a/src/typings/stripHtmlComments.d.ts b/src/typings/stripHtmlComments.d.ts
new file mode 100644
index 00000000..4c72376a
--- /dev/null
+++ b/src/typings/stripHtmlComments.d.ts
@@ -0,0 +1,4 @@
+declare module 'strip-html-comments' {
+	function stripHtmlComments(text: string): string;
+	export = stripHtmlComments;
+}


Test patch:
diff --git a/src/client/rsg-components/slots/__snapshots__/IsolateButton.spec.js.snap b/src/client/rsg-components/slots/__snapshots__/IsolateButton.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/slots/__snapshots__/IsolateButton.spec.js.snap
rename to src/client/rsg-components/slots/__snapshots__/IsolateButton.spec.tsx.snap
diff --git a/src/client/rsg-components/slots/__snapshots__/UsageTabButton.spec.js.snap b/src/client/rsg-components/slots/__snapshots__/UsageTabButton.spec.tsx.snap
similarity index 100%
rename from src/client/rsg-components/slots/__snapshots__/UsageTabButton.spec.js.snap
rename to src/client/rsg-components/slots/__snapshots__/UsageTabButton.spec.tsx.snap
diff --git a/src/client/rsg-components/slots/index.js b/src/client/rsg-components/slots/index.ts
similarity index 100%
rename from src/client/rsg-components/slots/index.js
rename to src/client/rsg-components/slots/index.ts
diff --git a/src/client/utils/__tests__/__snapshots__/filterSectionsByName.spec.js.snap b/src/client/utils/__tests__/__snapshots__/filterSectionsByName.spec.ts.snap
similarity index 100%
rename from src/client/utils/__tests__/__snapshots__/filterSectionsByName.spec.js.snap
rename to src/client/utils/__tests__/__snapshots__/filterSectionsByName.spec.ts.snap
diff --git a/src/client/utils/__tests__/filterSectionsByName.spec.js b/src/client/utils/__tests__/filterSectionsByName.spec.ts
similarity index 91%
rename from src/client/utils/__tests__/filterSectionsByName.spec.js
rename to src/client/utils/__tests__/filterSectionsByName.spec.ts
index 5fe63c9c..716d4937 100644
--- a/src/client/utils/__tests__/filterSectionsByName.spec.js
+++ b/src/client/utils/__tests__/filterSectionsByName.spec.ts
@@ -1,7 +1,6 @@
-import deepfreeze from 'deepfreeze';
 import filterSectionsByName from '../filterSectionsByName';
 
-const sections = deepfreeze([
+const sections = [
 	{
 		name: 'General',
 		sections: [
@@ -18,7 +17,7 @@ const sections = deepfreeze([
 			},
 		],
 	},
-]);
+];
 
 describe('filterSectionsByName', () => {
 	it('should recursively filter sections and components by name', () => {