Repository: styleguidist/react-styleguidist
Pull request #859 (2018-03-06 14:46:15)

feat: change template container id to prevent clashes
This PR addresses issue #753 with the following changes:

* use 'rsg-root' id insted of 'app' on the main container
* print deprecation warning to the console if 'app' id is used in custom template


Patch:
diff --git a/.github/Contributing.md b/.github/Contributing.md
index 560333e1..3c0ced56 100644
--- a/.github/Contributing.md
+++ b/.github/Contributing.md
@@ -52,6 +52,19 @@ npm run test:jest -- -u
 * We’re using [Prettier](https://github.com/prettier/prettier) to format JavaScript, so don’t worry much about code formatting.
 * Don’t commit generated files, like minified JavaScript.
 * Don’t change version number and change log.
+* If you're updating examples other then `examples/basic`, you'll need to modify your watch and start commands:
+
+```bash
+npm run compile:watch & npm run start:customised # if making changes to examples/customised
+npm run compile:watch & npm run start:sections # if making changes to examples/sections
+```
+See the `scripts` section of the top level [package.json](https://github.com/styleguidist/react-styleguidist/blob/master/package.json#L135)
+
+. If an example doesn't have a script just point to its config:
+
+```bash
+node bin/styleguidist.js server --config examples/path/to/example/styleguide.config.js
+```
 
 ## Need help?
 
diff --git a/.github/Issue_Template.md b/.github/Issue_Template.md
index db121390..3e6c6019 100644
--- a/.github/Issue_Template.md
+++ b/.github/Issue_Template.md
@@ -13,7 +13,8 @@ Make sure you’re using the latest versions of React Styleguidist.
 💀 Issues without a demo will be closed! 💀
 
 When something isn’t working for you, include a reproducible demo:
-  1. Fork the example project: https://github.com/styleguidist/example
+  1. Fork the example project repository: https://github.com/styleguidist/example
+     (please don’t use the main, styleguidist/react-styleguidist, repository).
   2. Modify it to reproduce the issue.
   3. Push to GitHub and paste the link here.
 
diff --git a/.gitignore b/.gitignore
index 67bfb784..b9dcbf79 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,5 +14,6 @@ Thumbs.db
 *.log
 .eslintcache
 yarn.lock
-cypress/screenshots/
-cypress/videos/
+/cypress/
+test/cypress/screenshots/
+test/cypress/videos/
diff --git a/.travis.yml b/.travis.yml
index a1f1392c..92537fb0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -49,9 +49,9 @@ script:
     fi
 after_success:
   # Make release with semantic-release if needed
-  - npm install -g semantic-release
+  - npm install -g semantic-release travis-deploy-once
   - npm install --no-save semantic-release-tamia
-  - semantic-release --analyze-commits semantic-release-tamia/analyzeCommits --verify-release semantic-release-tamia/verifyRelease --generate-notes semantic-release-tamia/generateNotes
+  - travis-deploy-once "semantic-release --analyze-commits semantic-release-tamia/analyzeCommits --verify-release semantic-release-tamia/verifyRelease --generate-notes semantic-release-tamia/generateNotes"
   - |
     if [[ "$TRAVIS_NODE_VERSION" == "9" ]]; then
       # Upload coverage report to Codecov
diff --git a/Readme.md b/Readme.md
index f564a9d2..9a7d4974 100644
--- a/Readme.md
+++ b/Readme.md
@@ -4,8 +4,7 @@
 
 **Isolated React component development environment with a living style guide**
 
-[![Build Status](https://travis-ci.org/styleguidist/react-styleguidist.svg)](https://travis-ci.org/styleguidist/react-styleguidist) [![npm](https://img.shields.io/npm/v/react-styleguidist.svg)](https://www.npmjs.com/package/react-styleguidist) [![Join the chat at https://gitter.im/styleguidist/styleguidist](https://badges.gitter.im/styleguidist/styleguidist.svg)](https://gitter.im/styleguidist/styleguidist?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the community on Spectrum](https://withspectrum.github.io/badge/badge.svg)](https://spectrum.chat/styleguidist) [![Codecov](https://codecov.io/gh/styleguidist/react-styleguidist/branch/master/graph/badge.svg)](https://codecov.io/gh/styleguidist/react-styleguidist)
-[![Open Source Helpers](https://www.codetriage.com/styleguidist/react-styleguidist/badges/users.svg)](https://www.codetriage.com/styleguidist/react-styleguidist)
+[![Build Status](https://travis-ci.org/styleguidist/react-styleguidist.svg)](https://travis-ci.org/styleguidist/react-styleguidist) [![npm](https://img.shields.io/npm/v/react-styleguidist.svg)](https://www.npmjs.com/package/react-styleguidist) [![Join the chat at https://gitter.im/styleguidist/styleguidist](https://badges.gitter.im/styleguidist/styleguidist.svg)](https://gitter.im/styleguidist/styleguidist?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Codecov](https://codecov.io/gh/styleguidist/react-styleguidist/branch/master/graph/badge.svg)](https://codecov.io/gh/styleguidist/react-styleguidist) [![Open Source Helpers](https://www.codetriage.com/styleguidist/react-styleguidist/badges/users.svg)](https://www.codetriage.com/styleguidist/react-styleguidist)
 
 </div>
 
diff --git a/cypress.json b/cypress.json
index 0e255134..fdff325b 100644
--- a/cypress.json
+++ b/cypress.json
@@ -1,3 +1,9 @@
 {
-  "baseUrl": "http://localhost:8082"
+  "baseUrl": "http://localhost:8082",
+  "fixturesFolder": "test/cypress/fixtures",
+  "integrationFolder": "test/cypress/integration",
+  "pluginsFile": "test/cypress/plugins/index.js",
+  "screenshotsFolder": "test/cypress/screenshots",
+  "videosFolder": "test/cypress/videos",
+  "supportFile": "test/cypress/support/index.js"
 }
diff --git a/docs/Components.md b/docs/Components.md
index 97b7fafc..9946bf7b 100644
--- a/docs/Components.md
+++ b/docs/Components.md
@@ -5,6 +5,7 @@
 <!-- toc -->
 
 * [Finding components](#finding-components)
+* [Loading and exposing components](#loading-and-exposing-components)
 * [Sections](#sections)
 * [Limitations](#limitations)
 
@@ -41,6 +42,63 @@ module.exports = {
 
 > **Note:** Use [getComponentPathLine](Configuration.md#getcomponentpathline) option to change a path you see below a component name.
 
+## Loading and exposing components
+
+Styleguidist _loads_ your components and _exposes_ them globally for your examples to consume.
+
+### Identifier
+
+It will try to use the `displayName` of your component as the identifier. If it cannot understand a `displayName` (for example if it is dynamically generated), it will fall back to something it can understand.
+
+In each of the following cases, the global identifier will be `Component`.
+
+| Path | Code | Styleguidist understands |
+| ---- | ---- | ------------------------ |
+| /whatever.js | `export default function Component() { ... }` | displayName |
+| /whatever.js | `export default function SomeName() { ... }`<br>`SomeName.displayName = 'Component';` | displayName |
+| /whatever.js | `export default function Component() { ... }`<br>`Component.displayName = dynamicNamer();` | displayName at declaration
+| /component.js | `const name = 'SomeName';`<br>`const componentMap = {`<br>`[name]: function() { ... }`<br>`};`<br>`export default componentMap[name];` | File name |
+| /component/index.js | `const name = 'SomeName';`<br>`const componentMap = {`<br>`[name]: function() { ... }`<br>`};`<br>`export default componentMap[name];` | Folder name |
+
+
+### Default vs named exports
+
+Stylegudist will use an ECMAScript module’s `default` export or CommonJS `module.exports` if they are defined.
+
+```javascript
+// /component.js
+export default function Component() { ... }
+// will be exposed globally as Component
+
+// /component.js
+function Component() { ... }
+module.exports = Component;
+// will be exposed globally as Component
+```
+
+If you use only named exports, Styleguidist will expose named exports from modules as follows...
+
+If there is only one named export, it will expose that.
+
+```javascript
+// /component.js
+export function Component() { ... }
+// will be exposed globally as Component
+```
+
+If there are several named exports, it will expose the named export which has the same name as the understood identifier.
+
+```javascript
+// /component.js
+export function someUtil() { ... }
+// will not be exposed
+
+export function Component() { ... }
+// will be exposed globally as Component
+```
+
+If you export several React components as named exports from a single module, Styleguidist is likely to behave unreliably. If it cannot understand which named export to expose, you may not be able to access that export.
+
 ## Sections
 
 Group components into sections or add extra Markdown documents to your style guide.
@@ -49,7 +107,7 @@ Each section consists of (all fields are optional):
 
 * `name` — section title.
 * `content` — location of a Markdown file containing the overview content.
-* `components` — a glob pattern string or a function returning a list of components. The same rules apply as for the root `components` option.
+* `components` — a glob pattern string, an array of component paths or a function returning a list of components. The same rules apply as for the root `components` option.
 * `sections` — array of subsections (can be nested).
 * `description` — A small description of this section.
 * `ignore` — string/array of globs that should not be included in the section.
diff --git a/docs/Configuration.md b/docs/Configuration.md
index 0ddc62e0..8aea7889 100644
--- a/docs/Configuration.md
+++ b/docs/Configuration.md
@@ -20,6 +20,7 @@ By default, Styleguidist will look for `styleguide.config.js` file in your proje
 * [`handlers`](#handlers)
 * [`ignore`](#ignore)
 * [`logger`](#logger)
+* [`pagePerSection`](#pagepersection)
 * [`previewDelay`](#previewdelay)
 * [`propsParser`](#propsparser)
 * [`require`](#require)
@@ -37,6 +38,7 @@ By default, Styleguidist will look for `styleguide.config.js` file in your proje
 * [`template`](#template)
 * [`theme`](#theme)
 * [`title`](#title)
+* [`transformProps`](#transformprops)
 * [`updateExample`](#updateexample)
 * [`verbose`](#verbose)
 * [`webpackConfig`](#webpackconfig)
@@ -57,10 +59,11 @@ Styleguidist uses [Bublé](https://buble.surge.sh/guide/) to run ES6 code on the
 
 #### `components`
 
-Type: `String` or `Function`, default: `src/components/**/*.{js,jsx,ts,tsx}`
+Type: `String`, `Function` or `Array`, default: `src/components/**/*.{js,jsx,ts,tsx}`
 
 * when `String`: a [glob pattern](https://github.com/isaacs/node-glob#glob-primer) that matches all your component modules.
 * when `Function`: a function that returns an array of module paths.
+* when `Array`: an array of module paths.
 
 All paths are relative to config folder.
 
@@ -259,6 +262,22 @@ module.exports = {
 }
 ```
 
+#### `pagePerSection`
+
+Type: `Boolean`, default: `false`
+
+Render one section or component per page, starting with the first.
+
+If set to `true`, the sidebar will be visible on each page, except for the examples.
+
+The value may be differ on each environment.
+
+```javascript
+module.exports = {
+  pagePerSection: process.env.NODE_ENV !== 'production'
+}
+```
+
 #### `previewDelay`
 
 Type: `Number`, default: 500
@@ -430,6 +449,20 @@ Type: `String`, default: `<app name from package.json> Style Guide`
 
 Style guide title.
 
+#### `transformProps`
+
+Type: `Function`, optional
+
+Function that transforms component properties. By default properties are sorted such that required properties come first, optional prameters come second. Properties in both groups are sorted by their property names.
+
+To disable sorting the identity function can be used:
+
+```javascript
+module.exports = {
+  transformProps: props => props
+}
+```
+
 #### `updateExample`
 
 Type: `Function`, optional
diff --git a/docs/Cookbook.md b/docs/Cookbook.md
index 6f1184d4..7cec3225 100644
--- a/docs/Cookbook.md
+++ b/docs/Cookbook.md
@@ -23,7 +23,7 @@
 * [How to add external JavaScript and CSS files?](#how-to-add-external-javascript-and-css-files)
 * [How to reuse project’s webpack config?](#how-to-reuse-projects-webpack-config)
 * [How to use React Styleguidist with Redux, Relay or Styled Components?](#how-to-use-react-styleguidist-with-redux-relay-or-styled-components)
-* [What’s the difference betweeen Styleguidist and Storybook](#whats-the-difference-betweeen-styleguidist-and-storybook)
+* [What’s the difference between Styleguidist and Storybook?](#whats-the-difference-between-styleguidist-and-storybook)
 * [Are there any other projects like this?](#are-there-any-other-projects-like-this)
 
 <!-- tocstop -->
@@ -114,7 +114,6 @@ const iconElements = Object.keys(icons).map(iconName => {
     </span>
   )
 })
-
 ;<div>{iconElements}</div>
 ```
 
diff --git a/docs/Documenting.md b/docs/Documenting.md
index 14ecc63a..d243ae3d 100644
--- a/docs/Documenting.md
+++ b/docs/Documenting.md
@@ -51,7 +51,7 @@ export default class Button extends React.Component {
 
 > **Note:** You can change its behavior using [propsParser](Configuration.md#propsparser) and [resolver](Configuration.md#resolver) options.
 
-> **Note:** Component’s `PropTypes` and documentation comments are parsed by the [react-docgen](https://github.com/reactjs/react-docgen) library.
+> **Note:** Component’s `PropTypes` and documentation comments are parsed by the [react-docgen](https://github.com/reactjs/react-docgen) library. They can be modified using the [transformProps](Configuration.md#transformprops) function.
 
 ## Usage examples and Readme files
 
diff --git a/docs/Thirdparties.md b/docs/Thirdparties.md
index 6afcf435..6b42f786 100644
--- a/docs/Thirdparties.md
+++ b/docs/Thirdparties.md
@@ -17,7 +17,7 @@
 
 ## How Styleguidist works
 
-Styleguidist always uses the default export to _load_ your components but it uses [react-docgen](https://github.com/reactjs/react-docgen) to _generate documentation_ which may require changes in your code to work properly.
+Styleguidist _loads_ your components (see [Loading and exposing components](Components.md#loading-and-exposing-components) for more) but it uses [react-docgen](https://github.com/reactjs/react-docgen) to _generate documentation_ which may require changes in your code to work properly.
 
 React-docgen reads your components as static text files and looks for patterns like class or function declarations that looks like React components. It does not run any JavaScript code, so, if your component is dynamically generated, is wrapped in a higher-order component, or is split into several files, then react-docgen may not understand it.
 
diff --git a/loaders/props-loader.js b/loaders/props-loader.js
index 65cfb7ec..0857658e 100644
--- a/loaders/props-loader.js
+++ b/loaders/props-loader.js
@@ -8,6 +8,7 @@ const toAst = require('to-ast');
 const logger = require('glogg')('rsg');
 const getExamples = require('./utils/getExamples');
 const getProps = require('./utils/getProps');
+const sortProps = require('./utils/sortProps');
 const consts = require('../scripts/consts');
 
 const ERROR_MISSING_DEFINITION = 'No suitable component definition found.';
@@ -24,6 +25,7 @@ module.exports = function(source) {
 	const defaultParser = (filePath, source, resolver, handlers) =>
 		reactDocs.parse(source, resolver, handlers);
 	const propsParser = config.propsParser || defaultParser;
+	const propsTransform = config.propsTransform || sortProps;
 
 	let props = {};
 	try {
@@ -53,6 +55,19 @@ module.exports = function(source) {
 
 	props = getProps(props, file);
 
+	const componentProps = props.props;
+	if (componentProps) {
+		// Transform the properties to an array. This will allow for sorting.
+		const propsAsArray = Object.keys(componentProps).reduce((acc, name) => {
+			componentProps[name].name = name;
+			acc.push(componentProps[name]);
+			return acc;
+		}, []);
+
+		// Pipe through transform method and override component properties.
+		props.props = propsTransform(propsAsArray);
+	}
+
 	// Examples from Markdown file
 	const examplesFile = config.getExampleFilename(file);
 	props.examples = getExamples(examplesFile, props.displayName, config.defaultExample);
diff --git a/loaders/styleguide-loader.js b/loaders/styleguide-loader.js
index 4e7a24de..6ae37024 100644
--- a/loaders/styleguide-loader.js
+++ b/loaders/styleguide-loader.js
@@ -24,6 +24,7 @@ const CLIENT_CONFIG_OPTIONS = [
 	'styles',
 	'compilerConfig',
 	'editorConfig',
+	'pagePerSection',
 ];
 
 module.exports = function() {};
diff --git a/loaders/utils/getComponentFiles.js b/loaders/utils/getComponentFiles.js
index cd6524f5..9fa4cfcb 100644
--- a/loaders/utils/getComponentFiles.js
+++ b/loaders/utils/getComponentFiles.js
@@ -21,11 +21,13 @@ module.exports = function getComponentFiles(components, rootDir, ignore) {
 	let componentFiles;
 	if (isFunction(components)) {
 		componentFiles = components();
+	} else if (Array.isArray(components)) {
+		componentFiles = components;
 	} else if (isString(components)) {
 		componentFiles = glob.sync(path.resolve(rootDir, components), { ignore });
 	} else {
 		throw new Error(
-			`Styleguidist: components should be string or function, received ${typeof components}.`
+			`Styleguidist: components should be string, function or array, received ${typeof components}.`
 		);
 	}
 
diff --git a/loaders/utils/sortProps.js b/loaders/utils/sortProps.js
new file mode 100644
index 00000000..934263db
--- /dev/null
+++ b/loaders/utils/sortProps.js
@@ -0,0 +1,19 @@
+'use strict';
+
+const sortBy = require('lodash/sortBy');
+
+/**
+ * Sorts an array of properties by their 'required' property first and 'name'
+ * property second.
+ *
+ * @param {array} props
+ * @return {array} Sorted properties
+ */
+function sortProps(props) {
+	const requiredPropNames = sortBy(props.filter(prop => prop.required), 'name');
+	const optionalPropNames = sortBy(props.filter(prop => !prop.required), 'name');
+	const sortedProps = requiredPropNames.concat(optionalPropNames);
+	return sortedProps;
+}
+
+module.exports = sortProps;
diff --git a/package-lock.json b/package-lock.json
index 82d9fce9..1c6a10f3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "react-styleguidist",
-  "version": "6.1.0",
+  "version": "0.0.0-development",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -5736,12 +5736,6 @@
         "har-schema": "2.0.0"
       }
     },
-    "harmony-reflect": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.5.1.tgz",
-      "integrity": "sha1-tUymF7AMyK71Wbuxez2FQx3H4yk=",
-      "dev": true
-    },
     "has": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz",
@@ -6127,15 +6121,6 @@
         }
       }
     },
-    "identity-obj-proxy": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz",
-      "integrity": "sha1-lNK9qWCERT7zb7xarsN+D3nx/BQ=",
-      "dev": true,
-      "requires": {
-        "harmony-reflect": "1.5.1"
-      }
-    },
     "ieee754": {
       "version": "1.1.8",
       "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
diff --git a/package.json b/package.json
index 964fa6ef..b56f0fae 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "react-styleguidist",
   "description": "React components style guide generator",
-  "version": "6.1.0",
+  "version": "0.0.0-development",
   "homepage": "https://github.com/styleguidist/react-styleguidist",
   "author": {
     "name": "Artem Sapegin",
@@ -118,7 +118,6 @@
     "eslint-plugin-react": "^7.5.1",
     "file-loader": "^1.1.5",
     "husky": "^0.14.3",
-    "identity-obj-proxy": "^3.0.0",
     "jest": "^21.2.1",
     "jest-serializer-html": "^4.0.1",
     "keymirror": "^0.1.1",
@@ -175,17 +174,16 @@
       "./src"
     ],
     "moduleNameMapper": {
-      "^.+\\.css$": "identity-obj-proxy"
+      "^.+\\.css$": "<rootDir>/test/empty.js"
     },
     "testPathIgnorePatterns": [
       "<rootDir>/lib/",
-      "<rootDir>/examples/"
+      "<rootDir>/examples/",
+      "<rootDir>/test/"
     ],
     "coveragePathIgnorePatterns": [
-      "<rootDir>/src/utils/markdown-to-jsx.js",
       "<rootDir>/scripts/server.js",
-      "<rootDir>/scripts/build.js",
-      "<rootDir>/test/jestsetup.js"
+      "<rootDir>/scripts/build.js"
     ],
     "snapshotSerializers": [
       "deabsdeep/serializer",
diff --git a/scripts/schemas/config.js b/scripts/schemas/config.js
index 453b0cda..cb9b4211 100644
--- a/scripts/schemas/config.js
+++ b/scripts/schemas/config.js
@@ -124,6 +124,10 @@ module.exports = {
 	logger: {
 		type: 'object',
 	},
+	pagePerSection: {
+		type: 'boolean',
+		default: false,
+	},
 	previewDelay: {
 		type: 'number',
 		default: 500,
@@ -235,6 +239,9 @@ module.exports = {
 		},
 		example: 'My Style Guide',
 	},
+	transformProps: {
+		type: 'function',
+	},
 	updateExample: {
 		type: 'function',
 		default: props => {
diff --git a/scripts/templates/index.html b/scripts/templates/index.html
index 11ddb94a..122fa071 100644
--- a/scripts/templates/index.html
+++ b/scripts/templates/index.html
@@ -6,6 +6,6 @@
 		<meta name="viewport" content="width=device-width, initial-scale=1.0">
 	</head>
 	<body>
-		<div id="app"></div>
+		<div id="rsg-root"></div>
 	</body>
 </html>
diff --git a/src/index.js b/src/index.js
index f7e44ebc..564b249b 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,20 +1,38 @@
 /* eslint-disable import/first */
-
 import './polyfills';
 import './styles';
 import ReactDOM from 'react-dom';
 import renderStyleguide from './utils/renderStyleguide';
 
 // Examples code revision to rerender only code examples (not the whole page) when code changes
+// eslint-disable-next-line no-unused-vars
 let codeRevision = 0;
 
+/** Scrolls to origin when current window location hash points to an isolated view. */
+const scrollToOrigin = () => {
+	if (window.location.hash.indexOf('#!/') === 0) {
+		window.scrollTo(0, 0);
+	}
+};
+
 const render = () => {
 	// eslint-disable-next-line import/no-unresolved
 	const styleguide = require('!!../loaders/styleguide-loader!./index.js');
-	ReactDOM.render(renderStyleguide(styleguide, codeRevision), document.getElementById('app'));
+	let containerId = 'rsg-root';
+
+	if (document.getElementById('app')) {
+		// eslint-disable-next-line no-console
+		console.warn(
+			"The use of 'app' element id in the template is deprecated. Please, update your template file to use 'rsg-root' as the container id."
+		);
+		containerId = 'app';
+	}
+
+	ReactDOM.render(renderStyleguide(styleguide, codeRevision), document.getElementById(containerId));
 };
 
 window.addEventListener('hashchange', render);
+window.addEventListener('hashchange', scrollToOrigin);
 
 /* istanbul ignore if */
 if (module.hot) {
diff --git a/src/rsg-components/Argument/__snapshots__/Argument.spec.js.snap b/src/rsg-components/Argument/__snapshots__/Argument.spec.js.snap
index 43e86baf..b7340b41 100644
--- a/src/rsg-components/Argument/__snapshots__/Argument.spec.js.snap
+++ b/src/rsg-components/Argument/__snapshots__/Argument.spec.js.snap
@@ -14,7 +14,7 @@ exports[`should render argument 1`] = `
     Array
   </Styled(Type)>
    — 
-  <Styled(Markdown)
+  <Markdown
     inline={true}
     text="Converts foo to bar"
   />
@@ -46,7 +46,7 @@ exports[`should render argument without type 1`] = `
       Foo
     </Styled(Name)>
   </span>
-  <Styled(Markdown)
+  <Markdown
     inline={true}
     text="Converts foo to bar"
   />
@@ -62,7 +62,7 @@ exports[`should render return value 1`] = `
     Array
   </Styled(Type)>
    — 
-  <Styled(Markdown)
+  <Markdown
     inline={true}
     text="Converts foo to bar"
   />
diff --git a/src/rsg-components/ComponentsList/ComponentsList.js b/src/rsg-components/ComponentsList/ComponentsList.js
new file mode 100644
index 00000000..dcad24b9
--- /dev/null
+++ b/src/rsg-components/ComponentsList/ComponentsList.js
@@ -0,0 +1,25 @@
+import React from 'react';
+import ComponentsListRenderer from 'rsg-components/ComponentsList/ComponentsListRenderer';
+import PropTypes from 'prop-types';
+import getUrl from '../../utils/getUrl';
+
+function ComponentsList({ classes, items, useIsolatedLinks = false }) {
+	const mappedItems = items.map(item => ({
+		...item,
+		href: getUrl({
+			name: item.name,
+			slug: item.slug,
+			anchor: !useIsolatedLinks,
+			isolated: useIsolatedLinks,
+		}),
+	}));
+	return <ComponentsListRenderer classes={classes} items={mappedItems} />;
+}
+
+ComponentsList.propTypes = {
+	items: PropTypes.array.isRequired,
+	classes: PropTypes.object,
+	useIsolatedLinks: PropTypes.bool,
+};
+
+export default ComponentsList;
diff --git a/src/rsg-components/ComponentsList/ComponentsList.spec.js b/src/rsg-components/ComponentsList/ComponentsList.spec.js
index a6fa3168..c416d0b7 100644
--- a/src/rsg-components/ComponentsList/ComponentsList.spec.js
+++ b/src/rsg-components/ComponentsList/ComponentsList.spec.js
@@ -1,15 +1,50 @@
 import React from 'react';
+import ComponentsList from './ComponentsList';
 import { ComponentsListRenderer } from './ComponentsListRenderer';
 
+it('should set the correct href for items', () => {
+	const components = [
+		{
+			name: 'Button',
+			slug: 'button',
+		},
+		{
+			name: 'Input',
+			slug: 'input',
+		},
+	];
+
+	const actual = shallow(<ComponentsList items={components} classes={{}} />);
+	expect(actual).toMatchSnapshot();
+});
+
+it('should set the correct href for items when isolated links should be used', () => {
+	const components = [
+		{
+			name: 'Button',
+			slug: 'button',
+		},
+		{
+			name: 'Input',
+			slug: 'input',
+		},
+	];
+
+	const actual = shallow(<ComponentsList items={components} classes={{}} useIsolatedLinks />);
+	expect(actual).toMatchSnapshot();
+});
+
 it('should render sections with nested components', () => {
 	const components = [
 		{
 			name: 'Button',
 			slug: 'button',
+			href: '#button',
 		},
 		{
 			name: 'Input',
 			slug: 'input',
+			href: '#input',
 		},
 	];
 	const actual = shallow(<ComponentsListRenderer items={components} classes={{}} />);
@@ -28,9 +63,11 @@ it('should ignore items without name', () => {
 		{
 			name: 'Button',
 			slug: 'button',
+			href: '#button',
 		},
 		{
 			slug: 'input',
+			href: '#input',
 		},
 	];
 	const actual = shallow(<ComponentsListRenderer items={components} classes={{}} />);
diff --git a/src/rsg-components/ComponentsList/ComponentsListRenderer.js b/src/rsg-components/ComponentsList/ComponentsListRenderer.js
index 900aa1fd..4b376a22 100644
--- a/src/rsg-components/ComponentsList/ComponentsListRenderer.js
+++ b/src/rsg-components/ComponentsList/ComponentsListRenderer.js
@@ -42,12 +42,12 @@ export function ComponentsListRenderer({ classes, items }) {
 
 	return (
 		<ul className={classes.list}>
-			{items.map(({ heading, name, slug, content }) => (
+			{items.map(({ heading, name, href, content }) => (
 				<li
 					className={cx(classes.item, (!content || !content.props.items.length) && classes.isChild)}
 					key={name}
 				>
-					<Link className={cx(heading && classes.heading)} href={`#${slug}`}>
+					<Link className={cx(heading && classes.heading)} href={href}>
 						{name}
 					</Link>
 					{content}
@@ -60,6 +60,7 @@ export function ComponentsListRenderer({ classes, items }) {
 ComponentsListRenderer.propTypes = {
 	items: PropTypes.array.isRequired,
 	classes: PropTypes.object.isRequired,
+	useIsolatedLinks: PropTypes.bool,
 };
 
 export default Styled(styles)(ComponentsListRenderer);
diff --git a/src/rsg-components/ComponentsList/__snapshots__/ComponentsList.spec.js.snap b/src/rsg-components/ComponentsList/__snapshots__/ComponentsList.spec.js.snap
index 9a2fae29..8eb43c4a 100644
--- a/src/rsg-components/ComponentsList/__snapshots__/ComponentsList.spec.js.snap
+++ b/src/rsg-components/ComponentsList/__snapshots__/ComponentsList.spec.js.snap
@@ -42,3 +42,43 @@ exports[`should render sections with nested components 1`] = `
   </li>
 </ul>
 `;
+
+exports[`should set the correct href for items 1`] = `
+<Styled(ComponentsList)
+  classes={Object {}}
+  items={
+    Array [
+      Object {
+        "href": "blank#button",
+        "name": "Button",
+        "slug": "button",
+      },
+      Object {
+        "href": "blank#input",
+        "name": "Input",
+        "slug": "input",
+      },
+    ]
+  }
+/>
+`;
+
+exports[`should set the correct href for items when isolated links should be used 1`] = `
+<Styled(ComponentsList)
+  classes={Object {}}
+  items={
+    Array [
+      Object {
+        "href": "blank#!/Button",
+        "name": "Button",
+        "slug": "button",
+      },
+      Object {
+        "href": "blank#!/Input",
+        "name": "Input",
+        "slug": "input",
+      },
+    ]
+  }
+/>
+`;
diff --git a/src/rsg-components/ComponentsList/index.js b/src/rsg-components/ComponentsList/index.js
index 96588be8..564ab475 100644
--- a/src/rsg-components/ComponentsList/index.js
+++ b/src/rsg-components/ComponentsList/index.js
@@ -1,1 +1,1 @@
-export { default } from 'rsg-components/ComponentsList/ComponentsListRenderer';
+export { default } from 'rsg-components/ComponentsList/ComponentsList';
diff --git a/src/rsg-components/ExamplePlaceholder/__snapshots__/ExamplePlaceholder.spec.js.snap b/src/rsg-components/ExamplePlaceholder/__snapshots__/ExamplePlaceholder.spec.js.snap
index ae901d52..ca748e8c 100644
--- a/src/rsg-components/ExamplePlaceholder/__snapshots__/ExamplePlaceholder.spec.js.snap
+++ b/src/rsg-components/ExamplePlaceholder/__snapshots__/ExamplePlaceholder.spec.js.snap
@@ -9,7 +9,7 @@ exports[`should render a link 1`] = `
 `;
 
 exports[`should render an example placeholder after click 1`] = `
-<Styled(Markdown)
+<Markdown
   text="
 Create **Readme.md** or **Pizza.md** file in the component’s folder like this:
 
diff --git a/src/rsg-components/Examples/__snapshots__/Examples.spec.js.snap b/src/rsg-components/Examples/__snapshots__/Examples.spec.js.snap
index 7c8a234e..f70e9e19 100644
--- a/src/rsg-components/Examples/__snapshots__/Examples.spec.js.snap
+++ b/src/rsg-components/Examples/__snapshots__/Examples.spec.js.snap
@@ -13,7 +13,7 @@ exports[`should render examples 1`] = `
     key="1/0"
     name="button"
   />
-  <Styled(Markdown)
+  <Markdown
     key="1"
     text="Hello *world*!"
   />
diff --git a/src/rsg-components/JsDoc/__snapshots__/JsDoc.spec.js.snap b/src/rsg-components/JsDoc/__snapshots__/JsDoc.spec.js.snap
index 0d43bd86..42495007 100644
--- a/src/rsg-components/JsDoc/__snapshots__/JsDoc.spec.js.snap
+++ b/src/rsg-components/JsDoc/__snapshots__/JsDoc.spec.js.snap
@@ -1,7 +1,7 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`JsDoc should render Markdown 1`] = `
-<Styled(Markdown)
+<Markdown
   text="**Deprecated:** Use *another* method
 
 [See 1](#TestLink)
diff --git a/src/rsg-components/Markdown/Hr/Hr.spec.js b/src/rsg-components/Markdown/Hr/Hr.spec.js
new file mode 100644
index 00000000..c387e777
--- /dev/null
+++ b/src/rsg-components/Markdown/Hr/Hr.spec.js
@@ -0,0 +1,11 @@
+import React from 'react';
+
+import Hr from './index';
+
+describe('Markdown Hr', () => {
+	it('should render a horizontal rule', () => {
+		const actual = render(<Hr />);
+
+		expect(actual).toMatchSnapshot();
+	});
+});
diff --git a/src/rsg-components/Markdown/Hr/HrRenderer.js b/src/rsg-components/Markdown/Hr/HrRenderer.js
new file mode 100644
index 00000000..56351967
--- /dev/null
+++ b/src/rsg-components/Markdown/Hr/HrRenderer.js
@@ -0,0 +1,21 @@
+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/rsg-components/Markdown/Hr/__snapshots__/Hr.spec.js.snap b/src/rsg-components/Markdown/Hr/__snapshots__/Hr.spec.js.snap
new file mode 100644
index 00000000..aa6d63c1
--- /dev/null
+++ b/src/rsg-components/Markdown/Hr/__snapshots__/Hr.spec.js.snap
@@ -0,0 +1,7 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Markdown Hr should render a horizontal rule 1`] = `
+<hr
+  class="rsg--hr-0"
+/>
+`;
diff --git a/src/rsg-components/Markdown/Hr/index.js b/src/rsg-components/Markdown/Hr/index.js
new file mode 100644
index 00000000..db382d3a
--- /dev/null
+++ b/src/rsg-components/Markdown/Hr/index.js
@@ -0,0 +1,1 @@
+export { default } from 'rsg-components/Markdown/Hr/HrRenderer';
diff --git a/src/rsg-components/Markdown/Markdown.js b/src/rsg-components/Markdown/Markdown.js
index 7b7a8fa1..236bd928 100644
--- a/src/rsg-components/Markdown/Markdown.js
+++ b/src/rsg-components/Markdown/Markdown.js
@@ -1,17 +1,15 @@
 import PropTypes from 'prop-types';
 import { compiler } from 'markdown-to-jsx';
-import mapValues from 'lodash/mapValues';
-import memoize from 'lodash/memoize';
-import Styled from 'rsg-components/Styled';
 import Link from 'rsg-components/Link';
 import Text from 'rsg-components/Text';
-import Para, { styles as paraStyles } from 'rsg-components/Para';
+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 Pre 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 { Table, TableHead, TableBody, TableRow, TableCell } from 'rsg-components/Markdown/Table';
 
 // We’re explicitly specifying Webpack loaders here so we could skip specifying them in Webpack configuration.
@@ -19,154 +17,126 @@ import { Table, TableHead, TableBody, TableRow, TableCell } from 'rsg-components
 // eslint-disable-next-line import/no-unresolved
 require('!!../../../loaders/style-loader!../../../loaders/css-loader!highlight.js/styles/tomorrow.css');
 
-// Custom CSS classes for each tag: <em> → <em className={s.em}> + custom components
-const getBaseOverrides = memoize(classes => {
-	const styleOverrides = mapValues(classes, value => ({
+const baseOverrides = {
+	a: {
+		component: Link,
+	},
+	h1: {
+		component: MarkdownHeading,
 		props: {
-			className: value,
-		},
-	}));
-
-	return {
-		...styleOverrides,
-		a: {
-			component: Link,
-		},
-		h1: {
-			component: MarkdownHeading,
-			props: {
-				level: 1,
-			},
-		},
-		h2: {
-			component: MarkdownHeading,
-			props: {
-				level: 2,
-			},
-		},
-		h3: {
-			component: MarkdownHeading,
-			props: {
-				level: 3,
-			},
-		},
-		h4: {
-			component: MarkdownHeading,
-			props: {
-				level: 4,
-			},
-		},
-		h5: {
-			component: MarkdownHeading,
-			props: {
-				level: 5,
-			},
-		},
-		h6: {
-			component: MarkdownHeading,
-			props: {
-				level: 6,
-			},
-		},
-		p: {
-			component: Para,
-			props: {
-				semantic: 'p',
-			},
-		},
-		em: {
-			component: Text,
-			props: {
-				semantic: 'em',
-			},
-		},
-		strong: {
-			component: Text,
-			props: {
-				semantic: 'strong',
-			},
-		},
-		ul: {
-			component: List,
-		},
-		ol: {
-			component: List,
-			props: {
-				ordered: true,
-			},
+			level: 1,
 		},
-		blockquote: {
-			component: Blockquote,
-		},
-		code: {
-			component: Code,
-		},
-		pre: {
-			component: Pre,
+	},
+	h2: {
+		component: MarkdownHeading,
+		props: {
+			level: 2,
 		},
-		input: {
-			component: Checkbox,
+	},
+	h3: {
+		component: MarkdownHeading,
+		props: {
+			level: 3,
 		},
-		table: {
-			component: Table,
+	},
+	h4: {
+		component: MarkdownHeading,
+		props: {
+			level: 4,
 		},
-		thead: {
-			component: TableHead,
+	},
+	h5: {
+		component: MarkdownHeading,
+		props: {
+			level: 5,
 		},
-		th: {
-			component: TableCell,
-			props: {
-				header: true,
-			},
+	},
+	h6: {
+		component: MarkdownHeading,
+		props: {
+			level: 6,
 		},
-		tbody: {
-			component: TableBody,
+	},
+	p: {
+		component: Para,
+		props: {
+			semantic: 'p',
 		},
-		tr: {
-			component: TableRow,
+	},
+	em: {
+		component: Text,
+		props: {
+			semantic: 'em',
 		},
-		td: {
-			component: TableCell,
+	},
+	strong: {
+		component: Text,
+		props: {
+			semantic: 'strong',
 		},
-	};
-}, () => 'getBaseOverrides');
-
-// Inline mode: replace <p> (usual root component) with <span>
-const getInlineOverrides = memoize(classes => {
-	const overrides = getBaseOverrides(classes);
-
-	return {
-		...overrides,
-		p: {
-			component: Text,
+	},
+	ul: {
+		component: List,
+	},
+	ol: {
+		component: List,
+		props: {
+			ordered: true,
 		},
-	};
-}, () => 'getInlineOverrides');
-
-const styles = ({ space, fontFamily, color }) => ({
-	base: {
-		color: color.base,
-		fontFamily: fontFamily.base,
-		fontSize: 'inherit',
 	},
-	para: paraStyles({ space, color, fontFamily }).para,
+	blockquote: {
+		component: Blockquote,
+	},
+	code: {
+		component: Code,
+	},
+	pre: {
+		component: Pre,
+	},
+	input: {
+		component: Checkbox,
+	},
 	hr: {
-		composes: '$para',
-		borderWidth: [[0, 0, 1, 0]],
-		borderColor: color.border,
-		borderStyle: 'solid',
+		component: Hr,
+	},
+	table: {
+		component: Table,
+	},
+	thead: {
+		component: TableHead,
+	},
+	th: {
+		component: TableCell,
+		props: {
+			header: true,
+		},
+	},
+	tbody: {
+		component: TableBody,
 	},
-});
+	tr: {
+		component: TableRow,
+	},
+	td: {
+		component: TableCell,
+	},
+};
+
+const inlineOverrides = {
+	...baseOverrides,
+	p: {
+		component: Text,
+	},
+};
 
-function Markdown({ classes, text, inline }) {
-	const overrides = inline ? getInlineOverrides(classes) : getBaseOverrides(classes);
+function Markdown({ text, inline }) {
+	const overrides = inline ? inlineOverrides : baseOverrides;
 	return compiler(text, { overrides, forceBlock: true });
 }
 
 Markdown.propTypes = {
-	classes: PropTypes.object.isRequired,
 	text: PropTypes.string.isRequired,
 	inline: PropTypes.bool,
 };
 
-export default Styled(styles)(Markdown);
+export default Markdown;
diff --git a/src/rsg-components/Markdown/Markdown.spec.js b/src/rsg-components/Markdown/Markdown.spec.js
index 40917fa4..e663ab75 100644
--- a/src/rsg-components/Markdown/Markdown.spec.js
+++ b/src/rsg-components/Markdown/Markdown.spec.js
@@ -3,138 +3,136 @@ import { html } from 'cheerio';
 import Markdown from './Markdown';
 
 describe('Markdown', () => {
-	it('should render links', () => {
-		const markdown = 'a [link](http://test.com)';
-
+	const expectSnapshotToMatch = markdown => {
 		const actual = render(<Markdown text={markdown} />);
 
 		expect(html(actual)).toMatchSnapshot();
-	});
-
-	it('should render Markdown with custom CSS classes', () => {
-		const markdown = `
-# Header
+	};
 
-Text with *some* **formatting** and a [link](/foo).
-
-![Image](/bar.png)`;
-		const actual = render(<Markdown text={markdown} />);
-
-		expect(html(actual)).toMatchSnapshot();
-	});
+	it('should forward DOM attributes onto resulting HTML', () => {
+		const markdown =
+			'<a href="test.com" id="preserve-my-id" class="preserve-my-class">Something</a>';
 
-	it('should render Markdown in a p tag even for one paragraph', () => {
-		const actual = render(<Markdown text="pizza" />);
+		const actual = mount(<Markdown text={markdown} />);
 
-		expect(html(actual)).toMatchSnapshot();
+		expect(actual.find('a').props().id).toEqual('preserve-my-id');
+		expect(actual.find('a').props().className).toContain('preserve-my-class');
 	});
 
-	it('should render Markdown in span in inline mode', () => {
-		const markdown = 'Hello *world*!';
-		const actual = render(<Markdown text={markdown} inline />);
-
-		expect(html(actual)).toMatchSnapshot();
+	it('should render links', () => {
+		expectSnapshotToMatch('a [link](http://test.com)');
 	});
 
-	it('should render headings correctly', () => {
-		const markdown = `
+	it('should render headings', () => {
+		expectSnapshotToMatch(`
 # one
 ## two
 ### three
 #### four
 ##### five
 ###### six
-`;
-		const actual = render(<Markdown text={markdown} />);
+`);
+	});
 
-		expect(html(actual)).toMatchSnapshot();
+	it('should render paragraphs', () => {
+		expectSnapshotToMatch(`
+a paragraph
+
+another paragraph
+		`);
+	});
+
+	it('should render emphasis and strong text', () => {
+		expectSnapshotToMatch(`
+this text is **strong**
+
+and this is _emphasized_
+		`);
 	});
 
-	it('should render unordered lists correctly', () => {
-		const markdown = `
+	it('should render unordered lists', () => {
+		expectSnapshotToMatch(`
 * list
 * item
 * three
-`;
-		const actual = render(<Markdown text={markdown} />);
-
-		expect(html(actual)).toMatchSnapshot();
+`);
 	});
 
-	it('should render ordered lists correctly', () => {
-		const markdown = `
+	it('should render ordered lists', () => {
+		expectSnapshotToMatch(`
 1. list
 1. item
 1. three
-`;
-		const actual = render(<Markdown text={markdown} />);
-
-		expect(html(actual)).toMatchSnapshot();
+`);
 	});
 
-	it('should render check-lists correctly', () => {
-		const markdown = `
-* [ ] list 1
-* [ ] list 2
-* [x] list 3
-`;
-		const actual = render(<Markdown text={markdown} />);
-
-		expect(html(actual)).toMatchSnapshot();
-	});
-
-	it('should render mixed nested lists correctly', () => {
-		const markdown = `
+	it('should render mixed nested lists', () => {
+		expectSnapshotToMatch(`
 * list 1
 * list 2
   1. Sub-list
   1. Sub-list
   1. Sub-list
 * list 3
-`;
-		const actual = render(<Markdown text={markdown} />);
+`);
+	});
 
-		expect(html(actual)).toMatchSnapshot();
+	it('should render check-lists', () => {
+		expectSnapshotToMatch(`
+* [ ] to do 1
+* [ ] to do 2
+* [x] to do 3
+`);
+	});
+
+	it('should render a blockquote', () => {
+		expectSnapshotToMatch(`
+> This is a blockquote.
+> And this is a second line.
+`);
+	});
+
+	it('should render pre-formatted text', () => {
+		expectSnapshotToMatch(`
+    this is preformatted
+    so is this
+`);
 	});
 
 	it('should render code blocks without escaping', () => {
-		const markdown = `
+		expectSnapshotToMatch(`
 \`\`\`html
 <foo></foo>
 \`\`\`
-`;
-		const actual = render(<Markdown text={markdown} />);
-
-		expect(html(actual)).toMatchSnapshot();
+`);
 	});
 
 	it('should render inline code with escaping', () => {
-		const markdown = 'Foo `<bar>` baz';
-
-		const actual = render(<Markdown text={markdown} />);
-
-		expect(html(actual)).toMatchSnapshot();
+		expectSnapshotToMatch('Foo `<bar>` baz');
 	});
 
-	it('should render a blockquote', () => {
-		const markdown = `
-> This is a blockquote.
-> And this is a second line.
-`;
-		const actual = render(<Markdown text={markdown} />);
-
-		expect(html(actual)).toMatchSnapshot();
+	it('should render a horizontal rule', () => {
+		expectSnapshotToMatch(`---`);
 	});
 
 	it('should render a table', () => {
-		const markdown = `
+		expectSnapshotToMatch(`
 | heading 1 | heading 2 |
 | --------- | --------- |
 | foo		| bar		|
 | more foo	| more bar	|
-`;
-		const actual = render(<Markdown text={markdown} />);
+`);
+	});
+});
+
+describe('Markdown inline', () => {
+	const expectSnapshotToMatch = markdown => {
+		const actual = render(<Markdown text={markdown} inline />);
 
 		expect(html(actual)).toMatchSnapshot();
+	};
+
+	it('should render text in a span', () => {
+		expectSnapshotToMatch('Hello world!');
 	});
 });
diff --git a/src/rsg-components/Markdown/__snapshots__/Markdown.spec.js.snap b/src/rsg-components/Markdown/__snapshots__/Markdown.spec.js.snap
index 85ad3f0e..d9667f5d 100644
--- a/src/rsg-components/Markdown/__snapshots__/Markdown.spec.js.snap
+++ b/src/rsg-components/Markdown/__snapshots__/Markdown.spec.js.snap
@@ -1,62 +1,17 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`Markdown should render Markdown in a p tag even for one paragraph 1`] = `
+exports[`Markdown inline should render text in a span 1`] = `
 
-<p class="rsg--para-4">
-  pizza
-</p>
-
-`;
-
-exports[`Markdown should render Markdown in span in inline mode 1`] = `
-
-<span class="rsg--text-14 rsg--inheritSize-15 rsg--baseColor-19">
-  Hello
-  <em class="rsg--text-14 rsg--inheritSize-15 rsg--baseColor-19 rsg--em-21">
-    world
-  </em>
-  !
+<span class="rsg--text-11 rsg--inheritSize-12 rsg--baseColor-16">
+  Hello world!
 </span>
 
 `;
 
-exports[`Markdown should render Markdown with custom CSS classes 1`] = `
-
-<div>
-  <div class="rsg--spacing-6">
-    <h1 class="rsg--heading-7 rsg--heading1-8">
-      Header
-    </h1>
-  </div>
-  <p class="rsg--para-4">
-    Text with
-    <em class="rsg--text-14 rsg--inheritSize-15 rsg--baseColor-19 rsg--em-21">
-      some
-    </em>
-    <strong class="rsg--text-14 rsg--inheritSize-15 rsg--baseColor-19 rsg--strong-22">
-      formatting
-    </strong>
-    and a
-    <a href="/foo"
-       class="rsg--link-5"
-    >
-      link
-    </a>
-    .
-  </p>
-  <p class="rsg--para-4">
-    <img alt="Image"
-         src="/bar.png"
-    >
-  </p>
-</div>
-
-`;
-
 exports[`Markdown should render a blockquote 1`] = `
 
-<blockquote class="rsg--blockquote-30">
-  <p class="rsg--para-4">
+<blockquote class="rsg--blockquote-25">
+  <p class="rsg--para-2">
     This is a blockquote.
 And this is a second line.
   </p>
@@ -64,33 +19,39 @@ And this is a second line.
 
 `;
 
+exports[`Markdown should render a horizontal rule 1`] = `
+
+<hr class="rsg--hr-28">
+
+`;
+
 exports[`Markdown should render a table 1`] = `
 
-<table class="rsg--table-31">
-  <thead class="rsg--thead-32">
+<table class="rsg--table-29">
+  <thead class="rsg--thead-30">
     <tr>
-      <th class="rsg--th-34 rsg--td-33">
+      <th class="rsg--th-32 rsg--td-31">
         heading 1
       </th>
-      <th class="rsg--th-34 rsg--td-33">
+      <th class="rsg--th-32 rsg--td-31">
         heading 2
       </th>
     </tr>
   </thead>
   <tbody>
     <tr>
-      <td class="rsg--td-33">
+      <td class="rsg--td-31">
         foo
       </td>
-      <td class="rsg--td-33">
+      <td class="rsg--td-31">
         bar
       </td>
     </tr>
     <tr>
-      <td class="rsg--td-33">
+      <td class="rsg--td-31">
         more foo
       </td>
-      <td class="rsg--td-33">
+      <td class="rsg--td-31">
         more bar
       </td>
     </tr>
@@ -99,30 +60,30 @@ exports[`Markdown should render a table 1`] = `
 
 `;
 
-exports[`Markdown should render check-lists correctly 1`] = `
+exports[`Markdown should render check-lists 1`] = `
 
-<ul class="rsg--list-24">
-  <li class="rsg--li-26">
+<ul class="rsg--list-21">
+  <li class="rsg--li-23">
     <input type="checkbox"
            readonly
-           class="rsg--input-27"
+           class="rsg--input-24"
     >
-    list 1
+    to do 1
   </li>
-  <li class="rsg--li-26">
+  <li class="rsg--li-23">
     <input type="checkbox"
            readonly
-           class="rsg--input-27"
+           class="rsg--input-24"
     >
-    list 2
+    to do 2
   </li>
-  <li class="rsg--li-26">
+  <li class="rsg--li-23">
     <input type="checkbox"
            checked
            readonly
-           class="rsg--input-27"
+           class="rsg--input-24"
     >
-    list 3
+    to do 3
   </li>
 </ul>
 
@@ -130,8 +91,8 @@ exports[`Markdown should render check-lists correctly 1`] = `
 
 exports[`Markdown should render code blocks without escaping 1`] = `
 
-<pre class="rsg--pre-28">
-  <code class="lang-html rsg--code-29">
+<pre class="rsg--pre-26">
+  <code class="lang-html rsg--code-27">
     <foo>
     </foo>
   </code>
@@ -139,36 +100,55 @@ exports[`Markdown should render code blocks without escaping 1`] = `
 
 `;
 
-exports[`Markdown should render headings correctly 1`] = `
+exports[`Markdown should render emphasis and strong text 1`] = `
 
 <div>
-  <div class="rsg--spacing-6">
-    <h1 class="rsg--heading-7 rsg--heading1-8">
+  <p class="rsg--para-2">
+    this text is
+    <strong class="rsg--text-11 rsg--inheritSize-12 rsg--baseColor-16 rsg--strong-19">
+      strong
+    </strong>
+  </p>
+  <p class="rsg--para-2">
+    and this is
+    <em class="rsg--text-11 rsg--inheritSize-12 rsg--baseColor-16 rsg--em-18">
+      emphasized
+    </em>
+  </p>
+</div>
+
+`;
+
+exports[`Markdown should render headings 1`] = `
+
+<div>
+  <div class="rsg--spacing-3">
+    <h1 class="rsg--heading-4 rsg--heading1-5">
       one
     </h1>
   </div>
-  <div class="rsg--spacing-6">
-    <h2 class="rsg--heading-7 rsg--heading2-9">
+  <div class="rsg--spacing-3">
+    <h2 class="rsg--heading-4 rsg--heading2-6">
       two
     </h2>
   </div>
-  <div class="rsg--spacing-6">
-    <h3 class="rsg--heading-7 rsg--heading3-10">
+  <div class="rsg--spacing-3">
+    <h3 class="rsg--heading-4 rsg--heading3-7">
       three
     </h3>
   </div>
-  <div class="rsg--spacing-6">
-    <h4 class="rsg--heading-7 rsg--heading4-11">
+  <div class="rsg--spacing-3">
+    <h4 class="rsg--heading-4 rsg--heading4-8">
       four
     </h4>
   </div>
-  <div class="rsg--spacing-6">
-    <h5 class="rsg--heading-7 rsg--heading5-12">
+  <div class="rsg--spacing-3">
+    <h5 class="rsg--heading-4 rsg--heading5-9">
       five
     </h5>
   </div>
-  <div class="rsg--spacing-6">
-    <h6 class="rsg--heading-7 rsg--heading6-13">
+  <div class="rsg--spacing-3">
+    <h6 class="rsg--heading-4 rsg--heading6-10">
       six
     </h6>
   </div>
@@ -178,9 +158,9 @@ exports[`Markdown should render headings correctly 1`] = `
 
 exports[`Markdown should render inline code with escaping 1`] = `
 
-<p class="rsg--para-4">
+<p class="rsg--para-2">
   Foo
-  <code class="rsg--code-29">
+  <code class="rsg--code-27">
     &lt;bar&gt;
   </code>
   baz
@@ -190,10 +170,10 @@ exports[`Markdown should render inline code with escaping 1`] = `
 
 exports[`Markdown should render links 1`] = `
 
-<p class="rsg--para-4">
+<p class="rsg--para-2">
   a
   <a href="http://test.com"
-     class="rsg--link-5"
+     class="rsg--link-0"
   >
     link
   </a>
@@ -201,59 +181,83 @@ exports[`Markdown should render links 1`] = `
 
 `;
 
-exports[`Markdown should render mixed nested lists correctly 1`] = `
+exports[`Markdown should render mixed nested lists 1`] = `
 
-<ul class="rsg--list-24">
-  <li class="rsg--li-26">
+<ul class="rsg--list-21">
+  <li class="rsg--li-23">
     list 1
   </li>
-  <li class="rsg--li-26">
+  <li class="rsg--li-23">
     list 2
-    <ol class="rsg--list-24 rsg--ordered-25">
-      <li class="rsg--li-26">
+    <ol class="rsg--list-21 rsg--ordered-22">
+      <li class="rsg--li-23">
         Sub-list
       </li>
-      <li class="rsg--li-26">
+      <li class="rsg--li-23">
         Sub-list
       </li>
-      <li class="rsg--li-26">
+      <li class="rsg--li-23">
         Sub-list
       </li>
     </ol>
   </li>
-  <li class="rsg--li-26">
+  <li class="rsg--li-23">
     list 3
   </li>
 </ul>
 
 `;
 
-exports[`Markdown should render ordered lists correctly 1`] = `
+exports[`Markdown should render ordered lists 1`] = `
 
-<ol class="rsg--list-24 rsg--ordered-25">
-  <li class="rsg--li-26">
+<ol class="rsg--list-21 rsg--ordered-22">
+  <li class="rsg--li-23">
     list
   </li>
-  <li class="rsg--li-26">
+  <li class="rsg--li-23">
     item
   </li>
-  <li class="rsg--li-26">
+  <li class="rsg--li-23">
     three
   </li>
 </ol>
 
 `;
 
-exports[`Markdown should render unordered lists correctly 1`] = `
+exports[`Markdown should render paragraphs 1`] = `
+
+<div>
+  <p class="rsg--para-2">
+    a paragraph
+  </p>
+  <p class="rsg--para-2">
+    another paragraph
+  </p>
+</div>
+
+`;
+
+exports[`Markdown should render pre-formatted text 1`] = `
+
+<pre class="rsg--pre-26">
+  <code class="rsg--code-27">
+    this is preformatted
+so is this
+  </code>
+</pre>
+
+`;
+
+exports[`Markdown should render unordered lists 1`] = `
 
-<ul class="rsg--list-24">
-  <li class="rsg--li-26">
+<ul class="rsg--list-21">
+  <li class="rsg--li-23">
     list
   </li>
-  <li class="rsg--li-26">
+  <li class="rsg--li-23">
     item
   </li>
-  <li class="rsg--li-26">
+  <li class="rsg--li-23">
     three
   </li>
 </ul>
diff --git a/src/rsg-components/Message/__snapshots__/Message.spec.js.snap b/src/rsg-components/Message/__snapshots__/Message.spec.js.snap
index 24062118..51f7344b 100644
--- a/src/rsg-components/Message/__snapshots__/Message.spec.js.snap
+++ b/src/rsg-components/Message/__snapshots__/Message.spec.js.snap
@@ -2,7 +2,7 @@
 
 exports[`renderer should render message 1`] = `
 <div>
-  <Styled(Markdown)
+  <Markdown
     text="Hello *world*!"
   />
 </div>
@@ -10,7 +10,7 @@ exports[`renderer should render message 1`] = `
 
 exports[`renderer should render message for array 1`] = `
 <div>
-  <Styled(Markdown)
+  <Markdown
     text="Hello *world*!
 Foo _bar_"
   />
diff --git a/src/rsg-components/Methods/__snapshots__/Methods.spec.js.snap b/src/rsg-components/Methods/__snapshots__/Methods.spec.js.snap
index 6516ccd3..fd8ccd93 100644
--- a/src/rsg-components/Methods/__snapshots__/Methods.spec.js.snap
+++ b/src/rsg-components/Methods/__snapshots__/Methods.spec.js.snap
@@ -150,7 +150,7 @@ exports[`PropsRenderer should render parameters 1`] = `
       key="2"
     >
       <div>
-        <Styled(Markdown)
+        <Markdown
           text="Public"
         />
         <JsDoc />
@@ -185,7 +185,7 @@ exports[`PropsRenderer should render public method 1`] = `
       key="2"
     >
       <div>
-        <Styled(Markdown)
+        <Markdown
           text="Public"
         />
         <JsDoc />
diff --git a/src/rsg-components/Playground/Playground.spec.js b/src/rsg-components/Playground/Playground.spec.js
index f02b2ced..0df35c82 100644
--- a/src/rsg-components/Playground/Playground.spec.js
+++ b/src/rsg-components/Playground/Playground.spec.js
@@ -23,7 +23,7 @@ const options = {
 			highlightTheme: 'base16-light',
 		},
 		codeRevision: 0,
-		slots,
+		slots: slots({}),
 	},
 	childContextTypes: {
 		slots: PropTypes.object.isRequired,
diff --git a/src/rsg-components/Props/Props.spec.js b/src/rsg-components/Props/Props.spec.js
index 21f6827c..cb769cfb 100644
--- a/src/rsg-components/Props/Props.spec.js
+++ b/src/rsg-components/Props/Props.spec.js
@@ -1,14 +1,16 @@
+/* eslint-disable react/prop-types */
 import React from 'react';
 import { parse } from 'react-docgen';
-import PropsRenderer, { propsToArray, columns } from './PropsRenderer';
+import PropsRenderer, { columns, getRowKey } from './PropsRenderer';
 import { unquote, getType, showSpaces } from './util';
 
+const propsToArray = props => Object.keys(props).map(name => ({ ...props[name], name }));
+
 // Test renderers with clean readable snapshot diffs
-// eslint-disable-next-line react/prop-types
 export default function ColumnsRenderer({ props }) {
 	return (
 		<ul>
-			{propsToArray(props).map((row, rowIdx) => (
+			{props.map((row, rowIdx) => (
 				<li key={rowIdx}>
 					{columns.map(({ render }, colIdx) => <div key={colIdx}>{render(row)}</div>)}
 				</li>
@@ -32,14 +34,32 @@ function render(propTypes, defaultProps = []) {
 			}
 		}
 	`);
-	return shallow(<ColumnsRenderer props={props.props} />);
+	return shallow(<ColumnsRenderer props={propsToArray(props.props)} />);
+}
+
+function renderFlow(propsType, defaultProps = []) {
+	const props = parse(`
+	  // @flow
+		import * as React from 'react';
+		type Props = {
+			${propsType.join(',')}
+		};
+		export default class Cmpnt extends React.Component<Props> {
+			static defaultProps = {
+				${defaultProps.join(',')}
+			}
+			render() {
+			}
+		}
+	`);
+	return shallow(<ColumnsRenderer props={propsToArray(props.props)} />);
 }
 
 describe('PropsRenderer', () => {
 	it('should render a table', () => {
 		const actual = shallow(
 			<PropsRenderer
-				props={{ color: { type: { name: 'string' }, required: false, description: '' } }}
+				props={[{ type: { name: 'string' }, required: false, description: '', name: 'color' }]}
 			/>
 		);
 
@@ -220,8 +240,9 @@ describe('props columns', () => {
 	});
 
 	it('should render arguments from JsDoc tags', () => {
-		const props = {
-			size: {
+		const props = [
+			{
+				name: 'size',
 				type: {
 					name: 'number',
 				},
@@ -242,15 +263,16 @@ describe('props columns', () => {
 					],
 				},
 			},
-		};
+		];
 		const actual = shallow(<ColumnsRenderer props={props} />);
 
 		expect(actual).toMatchSnapshot();
 	});
 
 	it('should render return from JsDoc tags', () => {
-		const getProps = tag => ({
-			size: {
+		const getProps = tag => [
+			{
+				name: 'size',
 				type: {
 					name: 'number',
 				},
@@ -266,7 +288,7 @@ describe('props columns', () => {
 					],
 				},
 			},
-		});
+		];
 
 		const actualForReturn = shallow(<ColumnsRenderer props={getProps('return')} />);
 
@@ -278,8 +300,9 @@ describe('props columns', () => {
 	});
 
 	it('should render name as deprecated when tag deprecated is present', () => {
-		const props = {
-			size: {
+		const props = [
+			{
+				name: 'size',
 				type: {
 					name: 'number',
 				},
@@ -294,11 +317,65 @@ describe('props columns', () => {
 					],
 				},
 			},
-		};
+		];
 		const actual = shallow(<ColumnsRenderer props={props} />);
 
 		expect(actual).toMatchSnapshot();
 	});
+
+	it('should render type string', () => {
+		const actual = renderFlow(['foo: string']);
+
+		expect(actual).toMatchSnapshot();
+	});
+
+	it('should render optional type string', () => {
+		const actual = renderFlow(['foo?: string']);
+
+		expect(actual).toMatchSnapshot();
+	});
+
+	it('should render type string with a default value', () => {
+		const actual = renderFlow(['foo?: string'], ['foo: "bar"']);
+
+		expect(actual).toMatchSnapshot();
+	});
+
+	it('should render literal type', () => {
+		const actual = renderFlow(['foo?: "bar"']);
+
+		expect(actual).toMatchSnapshot();
+	});
+
+	it('should render object type with body in tooltip', () => {
+		const actual = renderFlow(['foo: { bar: string }']);
+
+		expect(actual).toMatchSnapshot();
+	});
+
+	it('should render function type with body in tooltip', () => {
+		const actual = renderFlow(['foo: () => void']);
+
+		expect(actual).toMatchSnapshot();
+	});
+
+	it('should render union type with body in tooltip', () => {
+		const actual = renderFlow(['foo: "bar" | number']);
+
+		expect(actual).toMatchSnapshot();
+	});
+
+	it('should render tuple type with body in tooltip', () => {
+		const actual = renderFlow(['foo: ["bar", number]']);
+
+		expect(actual).toMatchSnapshot();
+	});
+
+	it('should render custom class type', () => {
+		const actual = renderFlow(['foo: React.ReactNode']);
+
+		expect(actual).toMatchSnapshot();
+	});
 });
 
 describe('unquote', () => {
@@ -334,3 +411,10 @@ describe('showSpaces', () => {
 		expect(result).toBe('␣pizza␣');
 	});
 });
+
+describe('getRowKey', () => {
+	it('should return type name', () => {
+		const result = getRowKey({ name: 'number' });
+		expect(result).toBe('number');
+	});
+});
diff --git a/src/rsg-components/Props/PropsRenderer.js b/src/rsg-components/Props/PropsRenderer.js
index 0c1e4db7..b32b0632 100644
--- a/src/rsg-components/Props/PropsRenderer.js
+++ b/src/rsg-components/Props/PropsRenderer.js
@@ -12,7 +12,6 @@ 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 map from 'lodash/map';
 import { unquote, getType, showSpaces } from './util';
 
 function renderType(type) {
@@ -34,6 +33,34 @@ function renderType(type) {
 	}
 }
 
+function renderFlowType(type) {
+	if (!type) {
+		return 'unknown';
+	}
+
+	const { name, raw, value } = type;
+
+	switch (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}>
+			<Type>{name}</Type>
+		</Text>
+	);
+}
+
 function renderEnum(prop) {
 	if (!Array.isArray(getType(prop).value)) {
 		return <span>{getType(prop).value}</span>;
@@ -76,19 +103,15 @@ function renderShape(props) {
 const defaultValueBlacklist = ['null', 'undefined'];
 
 function renderDefault(prop) {
-	if (prop.required) {
-		return (
-			<Text size="small" color="light">
-				Required
-			</Text>
-		);
-	} else if (prop.defaultValue) {
-		if (prop.type) {
-			const propName = prop.type.name;
+	// 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') {
+			} else if (propName === 'func' || propName === 'function') {
 				return (
 					<Text
 						size="small"
@@ -125,6 +148,12 @@ function renderDefault(prop) {
 		}
 
 		return <Code>{showSpaces(unquote(prop.defaultValue.value))}</Code>;
+	} else if (prop.required) {
+		return (
+			<Text size="small" color="light">
+				Required
+			</Text>
+		);
 	}
 	return '';
 }
@@ -198,6 +227,9 @@ function renderName(prop) {
 }
 
 function renderTypeColumn(prop) {
+	if (prop.flowType) {
+		return <Type>{renderFlowType(getType(prop))}</Type>;
+	}
 	return <Type>{renderType(getType(prop))}</Type>;
 }
 
@@ -205,10 +237,6 @@ export function getRowKey(row) {
 	return row.name;
 }
 
-export function propsToArray(props) {
-	return map(props, (prop, name) => ({ ...prop, name }));
-}
-
 export const columns = [
 	{
 		caption: 'Prop name',
@@ -229,9 +257,9 @@ export const columns = [
 ];
 
 export default function PropsRenderer({ props }) {
-	return <Table columns={columns} rows={propsToArray(props)} getRowKey={getRowKey} />;
+	return <Table columns={columns} rows={props} getRowKey={getRowKey} />;
 }
 
 PropsRenderer.propTypes = {
-	props: PropTypes.object.isRequired,
+	props: PropTypes.array.isRequired,
 };
diff --git a/src/rsg-components/Props/__snapshots__/Props.spec.js.snap b/src/rsg-components/Props/__snapshots__/Props.spec.js.snap
index 188fede2..d226acf6 100644
--- a/src/rsg-components/Props/__snapshots__/Props.spec.js.snap
+++ b/src/rsg-components/Props/__snapshots__/Props.spec.js.snap
@@ -573,7 +573,7 @@ exports[`props columns should render PropTypes.shape with description 1`] = `
               Required
             </Styled(Text)>
              — 
-            <Styled(Markdown)
+            <Markdown
               inline={true}
               text="Number"
             />
@@ -589,7 +589,7 @@ exports[`props columns should render PropTypes.shape with description 1`] = `
               any
             </Styled(Type)>
              — 
-            <Styled(Markdown)
+            <Markdown
               inline={true}
               text="Any"
             />
@@ -833,7 +833,7 @@ exports[`props columns should render arguments from JsDoc tags 1`] = `
       key="3"
     >
       <div>
-        <Styled(Markdown)
+        <Markdown
           text="Test description"
         />
         <JsDoc
@@ -879,6 +879,48 @@ exports[`props columns should render arguments from JsDoc tags 1`] = `
 </ul>
 `;
 
+exports[`props columns should render custom class type 1`] = `
+<ul>
+  <li
+    key="0"
+  >
+    <div
+      key="0"
+    >
+      <Styled(Name)
+        deprecated={false}
+      >
+        foo
+      </Styled(Name)>
+    </div>
+    <div
+      key="1"
+    >
+      <Styled(Type)>
+        React.ReactNode
+      </Styled(Type)>
+    </div>
+    <div
+      key="2"
+    >
+      <Styled(Text)
+        color="light"
+        size="small"
+      >
+        Required
+      </Styled(Text)>
+    </div>
+    <div
+      key="3"
+    >
+      <div>
+        <JsDoc />
+      </div>
+    </div>
+  </li>
+</ul>
+`;
+
 exports[`props columns should render description in Markdown 1`] = `
 <ul>
   <li
@@ -907,7 +949,7 @@ exports[`props columns should render description in Markdown 1`] = `
       key="3"
     >
       <div>
-        <Styled(Markdown)
+        <Markdown
           text="Label"
         />
         <JsDoc />
@@ -1039,6 +1081,91 @@ exports[`props columns should render function defaultValue as code when undefine
 </ul>
 `;
 
+exports[`props columns should render function type with body in tooltip 1`] = `
+<ul>
+  <li
+    key="0"
+  >
+    <div
+      key="0"
+    >
+      <Styled(Name)
+        deprecated={false}
+      >
+        foo
+      </Styled(Name)>
+    </div>
+    <div
+      key="1"
+    >
+      <Styled(Type)>
+        <Styled(Text)
+          size="small"
+          title="() => void"
+          underlined={true}
+        >
+          <Styled(Type)>
+            function
+          </Styled(Type)>
+        </Styled(Text)>
+      </Styled(Type)>
+    </div>
+    <div
+      key="2"
+    >
+      <Styled(Text)
+        color="light"
+        size="small"
+      >
+        Required
+      </Styled(Text)>
+    </div>
+    <div
+      key="3"
+    >
+      <div>
+        <JsDoc />
+      </div>
+    </div>
+  </li>
+</ul>
+`;
+
+exports[`props columns should render literal type 1`] = `
+<ul>
+  <li
+    key="0"
+  >
+    <div
+      key="0"
+    >
+      <Styled(Name)
+        deprecated={false}
+      >
+        foo
+      </Styled(Name)>
+    </div>
+    <div
+      key="1"
+    >
+      <Styled(Type)>
+        "bar"
+      </Styled(Type)>
+    </div>
+    <div
+      key="2"
+    />
+    <div
+      key="3"
+    >
+      <div>
+        <JsDoc />
+      </div>
+    </div>
+  </li>
+</ul>
+`;
+
 exports[`props columns should render name as deprecated when tag deprecated is present 1`] = `
 <ul>
   <li
@@ -1067,7 +1194,7 @@ exports[`props columns should render name as deprecated when tag deprecated is p
       key="3"
     >
       <div>
-        <Styled(Markdown)
+        <Markdown
           text="Test description"
         />
         <JsDoc
@@ -1086,6 +1213,91 @@ exports[`props columns should render name as deprecated when tag deprecated is p
 </ul>
 `;
 
+exports[`props columns should render object type with body in tooltip 1`] = `
+<ul>
+  <li
+    key="0"
+  >
+    <div
+      key="0"
+    >
+      <Styled(Name)
+        deprecated={false}
+      >
+        foo
+      </Styled(Name)>
+    </div>
+    <div
+      key="1"
+    >
+      <Styled(Type)>
+        <Styled(Text)
+          size="small"
+          title="{ bar: string }"
+          underlined={true}
+        >
+          <Styled(Type)>
+            object
+          </Styled(Type)>
+        </Styled(Text)>
+      </Styled(Type)>
+    </div>
+    <div
+      key="2"
+    >
+      <Styled(Text)
+        color="light"
+        size="small"
+      >
+        Required
+      </Styled(Text)>
+    </div>
+    <div
+      key="3"
+    >
+      <div>
+        <JsDoc />
+      </div>
+    </div>
+  </li>
+</ul>
+`;
+
+exports[`props columns should render optional type string 1`] = `
+<ul>
+  <li
+    key="0"
+  >
+    <div
+      key="0"
+    >
+      <Styled(Name)
+        deprecated={false}
+      >
+        foo
+      </Styled(Name)>
+    </div>
+    <div
+      key="1"
+    >
+      <Styled(Type)>
+        string
+      </Styled(Type)>
+    </div>
+    <div
+      key="2"
+    />
+    <div
+      key="3"
+    >
+      <div>
+        <JsDoc />
+      </div>
+    </div>
+  </li>
+</ul>
+`;
+
 exports[`props columns should render return from JsDoc tags 1`] = `
 <ul>
   <li
@@ -1114,7 +1326,7 @@ exports[`props columns should render return from JsDoc tags 1`] = `
       key="3"
     >
       <div>
-        <Styled(Markdown)
+        <Markdown
           text="Test description"
         />
         <JsDoc
@@ -1174,7 +1386,7 @@ exports[`props columns should render return from JsDoc tags 2`] = `
       key="3"
     >
       <div>
-        <Styled(Markdown)
+        <Markdown
           text="Test description"
         />
         <JsDoc
@@ -1206,6 +1418,190 @@ exports[`props columns should render return from JsDoc tags 2`] = `
 </ul>
 `;
 
+exports[`props columns should render tuple type with body in tooltip 1`] = `
+<ul>
+  <li
+    key="0"
+  >
+    <div
+      key="0"
+    >
+      <Styled(Name)
+        deprecated={false}
+      >
+        foo
+      </Styled(Name)>
+    </div>
+    <div
+      key="1"
+    >
+      <Styled(Type)>
+        <Styled(Text)
+          size="small"
+          title="[\\"bar\\", number]"
+          underlined={true}
+        >
+          <Styled(Type)>
+            tuple
+          </Styled(Type)>
+        </Styled(Text)>
+      </Styled(Type)>
+    </div>
+    <div
+      key="2"
+    >
+      <Styled(Text)
+        color="light"
+        size="small"
+      >
+        Required
+      </Styled(Text)>
+    </div>
+    <div
+      key="3"
+    >
+      <div>
+        <JsDoc />
+      </div>
+    </div>
+  </li>
+</ul>
+`;
+
+exports[`props columns should render type string 1`] = `
+<ul>
+  <li
+    key="0"
+  >
+    <div
+      key="0"
+    >
+      <Styled(Name)
+        deprecated={false}
+      >
+        foo
+      </Styled(Name)>
+    </div>
+    <div
+      key="1"
+    >
+      <Styled(Type)>
+        string
+      </Styled(Type)>
+    </div>
+    <div
+      key="2"
+    >
+      <Styled(Text)
+        color="light"
+        size="small"
+      >
+        Required
+      </Styled(Text)>
+    </div>
+    <div
+      key="3"
+    >
+      <div>
+        <JsDoc />
+      </div>
+    </div>
+  </li>
+</ul>
+`;
+
+exports[`props columns should render type string with a default value 1`] = `
+<ul>
+  <li
+    key="0"
+  >
+    <div
+      key="0"
+    >
+      <Styled(Name)
+        deprecated={false}
+      >
+        foo
+      </Styled(Name)>
+    </div>
+    <div
+      key="1"
+    >
+      <Styled(Type)>
+        string
+      </Styled(Type)>
+    </div>
+    <div
+      key="2"
+    >
+      <Styled(Code)>
+        bar
+      </Styled(Code)>
+    </div>
+    <div
+      key="3"
+    >
+      <div>
+        <JsDoc />
+      </div>
+    </div>
+  </li>
+</ul>
+`;
+
+exports[`props columns should render union type with body in tooltip 1`] = `
+<ul>
+  <li
+    key="0"
+  >
+    <div
+      key="0"
+    >
+      <Styled(Name)
+        deprecated={false}
+      >
+        foo
+      </Styled(Name)>
+    </div>
+    <div
+      key="1"
+    >
+      <Styled(Type)>
+        <Styled(Text)
+          size="small"
+          title="\\"bar\\" | number"
+          underlined={true}
+        >
+          <Styled(Type)>
+            union
+          </Styled(Type)>
+        </Styled(Text)>
+      </Styled(Type)>
+    </div>
+    <div
+      key="2"
+    >
+      <Styled(Text)
+        color="light"
+        size="small"
+      >
+        Required
+      </Styled(Text)>
+    </div>
+    <div
+      key="3"
+    >
+      <div>
+        <Styled(Para)>
+          <span />
+        </Styled(Para)>
+        <JsDoc />
+      </div>
+    </div>
+  </li>
+</ul>
+`;
+
 exports[`props columns should render unknown proptype for a prop when a relevant proptype is not assigned 1`] = `
 <ul>
   <li
diff --git a/src/rsg-components/ReactComponent/__snapshots__/ReactComponent.spec.js.snap b/src/rsg-components/ReactComponent/__snapshots__/ReactComponent.spec.js.snap
index 7ed8955a..e0b89aa9 100644
--- a/src/rsg-components/ReactComponent/__snapshots__/ReactComponent.spec.js.snap
+++ b/src/rsg-components/ReactComponent/__snapshots__/ReactComponent.spec.js.snap
@@ -3,7 +3,7 @@
 exports[`ReactComponent should pass rendered description, usage, examples, etc. to the renderer 1`] = `
 <Styled(ReactComponent)
   description={
-    <Styled(Markdown)
+    <Markdown
       text="Bar"
     />
   }
diff --git a/src/rsg-components/Section/SectionRenderer.js b/src/rsg-components/Section/SectionRenderer.js
index 695b6972..32c982f0 100644
--- a/src/rsg-components/Section/SectionRenderer.js
+++ b/src/rsg-components/Section/SectionRenderer.js
@@ -22,8 +22,8 @@ export function SectionRenderer(allProps) {
 			)}
 			{description && <Markdown text={description} />}
 			{content}
-			{components}
 			{sections}
+			{components}
 		</section>
 	);
 }
diff --git a/src/rsg-components/Section/__snapshots__/Section.spec.js.snap b/src/rsg-components/Section/__snapshots__/Section.spec.js.snap
index 9901f1c3..3d55ee99 100644
--- a/src/rsg-components/Section/__snapshots__/Section.spec.js.snap
+++ b/src/rsg-components/Section/__snapshots__/Section.spec.js.snap
@@ -67,14 +67,14 @@ exports[`render should render section 1`] = `
     }
     name="Foo"
   />
-  <Components
-    components={Array []}
-    depth={3}
-  />
   <Sections
     depth={3}
     sections={Array []}
   />
+  <Components
+    components={Array []}
+    depth={3}
+  />
 </section>
 `;
 
diff --git a/src/rsg-components/StyleGuide/StyleGuide.js b/src/rsg-components/StyleGuide/StyleGuide.js
index 7b63f7a9..a2cd8954 100644
--- a/src/rsg-components/StyleGuide/StyleGuide.js
+++ b/src/rsg-components/StyleGuide/StyleGuide.js
@@ -8,6 +8,29 @@ import Error from 'rsg-components/Error';
 import { HOMEPAGE } from '../../../scripts/consts';
 import { DisplayModes } from '../../consts';
 
+/**
+ * This function will return true, if the sidebar should be visible and false otherwise.
+ *
+ * These sorted conditions (highest precedence first) define the visibility
+ * state of the sidebar.
+ *
+ * - Sidebar is hidden for isolated example views
+ * - Sidebar is always visible when pagePerSection
+ * - 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 {boolean} showSidebar
+ * @param {boolean} pagePerSection
+ * @returns {boolean}
+ */
+function hasSidebar(displayMode, showSidebar, pagePerSection = false) {
+	return (
+		(pagePerSection && displayMode !== DisplayModes.example) ||
+		(showSidebar && displayMode === DisplayModes.all)
+	);
+}
+
 export default class StyleGuide extends Component {
 	static propTypes = {
 		codeRevision: PropTypes.number.isRequired,
@@ -17,6 +40,8 @@ export default class StyleGuide extends Component {
 		welcomeScreen: PropTypes.bool,
 		patterns: PropTypes.array,
 		displayMode: PropTypes.string,
+		allSections: PropTypes.array.isRequired,
+		pagePerSection: PropTypes.bool,
 	};
 
 	static childContextTypes = {
@@ -52,7 +77,15 @@ export default class StyleGuide extends Component {
 	}
 
 	render() {
-		const { config, sections, welcomeScreen, patterns, displayMode } = this.props;
+		const {
+			config,
+			sections,
+			welcomeScreen,
+			patterns,
+			displayMode,
+			allSections,
+			pagePerSection,
+		} = this.props;
 
 		if (this.state.error) {
 			return <Error error={this.state.error} info={this.state.info} />;
@@ -66,8 +99,8 @@ export default class StyleGuide extends Component {
 			<StyleGuideRenderer
 				title={config.title}
 				homepageUrl={HOMEPAGE}
-				toc={<TableOfContents sections={sections} />}
-				hasSidebar={config.showSidebar && displayMode === DisplayModes.all}
+				toc={<TableOfContents sections={allSections} useIsolatedLinks={pagePerSection} />}
+				hasSidebar={hasSidebar(displayMode, config.showSidebar, pagePerSection)}
 			>
 				<Sections sections={sections} depth={1} />
 			</StyleGuideRenderer>
diff --git a/src/rsg-components/StyleGuide/StyleGuide.spec.js b/src/rsg-components/StyleGuide/StyleGuide.spec.js
index aa9e01f2..85780e28 100644
--- a/src/rsg-components/StyleGuide/StyleGuide.spec.js
+++ b/src/rsg-components/StyleGuide/StyleGuide.spec.js
@@ -33,7 +33,13 @@ const config = {
 
 it('should render components list', () => {
 	const actual = shallow(
-		<StyleGuide codeRevision={1} config={config} sections={sections} slots={{}} />
+		<StyleGuide
+			codeRevision={1}
+			config={config}
+			sections={sections}
+			allSections={sections}
+			slots={{}}
+		/>
 	);
 
 	expect(actual).toMatchSnapshot();
@@ -41,14 +47,23 @@ it('should render components list', () => {
 
 it('should render welcome screen', () => {
 	const actual = shallow(
-		<StyleGuide codeRevision={1} config={config} sections={[]} slots={{}} welcomeScreen />
+		<StyleGuide
+			codeRevision={1}
+			config={config}
+			sections={[]}
+			allSections={[]}
+			slots={{}}
+			welcomeScreen
+		/>
 	);
 
 	expect(actual).toMatchSnapshot();
 });
 
 it('should render an error when componentDidCatch() is triggered', () => {
-	const wrapper = shallow(<StyleGuide codeRevision={1} config={config} sections={[]} slots={{}} />);
+	const wrapper = shallow(
+		<StyleGuide codeRevision={1} config={config} sections={[]} allSections={[]} slots={{}} />
+	);
 	wrapper
 		.instance()
 		.componentDidCatch({ toString: () => 'error' }, { componentStack: { toString: () => 'info' } });
@@ -59,7 +74,13 @@ it('should render an error when componentDidCatch() is triggered', () => {
 describe('sidebar rendering', () => {
 	it('renderer should have sidebar if showSidebar is not set', () => {
 		const wrapper = shallow(
-			<StyleGuide codeRevision={1} config={config} sections={sections} slots={{}} />
+			<StyleGuide
+				codeRevision={1}
+				config={config}
+				sections={sections}
+				allSections={sections}
+				slots={{}}
+			/>
 		);
 
 		expect(wrapper.prop('hasSidebar')).toEqual(true);
@@ -74,6 +95,7 @@ describe('sidebar rendering', () => {
 					showSidebar: false,
 				}}
 				sections={sections}
+				allSections={sections}
 				slots={{}}
 			/>
 		);
@@ -87,6 +109,7 @@ describe('sidebar rendering', () => {
 				codeRevision={1}
 				config={config}
 				sections={sections}
+				allSections={sections}
 				slots={{}}
 				displayMode={DisplayModes.component}
 			/>
@@ -94,6 +117,22 @@ describe('sidebar rendering', () => {
 
 		expect(wrapper.prop('hasSidebar')).toEqual(false);
 	});
+
+	it('renderer should have sidebar if pagePerSection is true', () => {
+		const wrapper = shallow(
+			<StyleGuide
+				codeRevision={1}
+				config={config}
+				sections={sections}
+				allSections={sections}
+				slots={{}}
+				displayMode={DisplayModes.component}
+				pagePerSection
+			/>
+		);
+
+		expect(wrapper.prop('hasSidebar')).toEqual(true);
+	});
 });
 
 it('renderer should render logo, table of contents and passed children', () => {
diff --git a/src/rsg-components/StyleGuide/__snapshots__/StyleGuide.spec.js.snap b/src/rsg-components/StyleGuide/__snapshots__/StyleGuide.spec.js.snap
index 79a0dbd9..374520e9 100644
--- a/src/rsg-components/StyleGuide/__snapshots__/StyleGuide.spec.js.snap
+++ b/src/rsg-components/StyleGuide/__snapshots__/StyleGuide.spec.js.snap
@@ -9,7 +9,7 @@ exports[`renderer should render logo, table of contents and passed children 1`]
       Content
     </h1>
     <footer>
-      <Styled(Markdown)
+      <Markdown
         text="Generated with [React Styleguidist](http://react-styleguidist.js.org/)"
       />
     </footer>
@@ -98,6 +98,7 @@ exports[`should render components list 1`] = `
           },
         ]
       }
+      useIsolatedLinks={undefined}
     />
   }
 >
diff --git a/src/rsg-components/TableOfContents/TableOfContents.js b/src/rsg-components/TableOfContents/TableOfContents.js
index 06f5ce58..01b6dc42 100644
--- a/src/rsg-components/TableOfContents/TableOfContents.js
+++ b/src/rsg-components/TableOfContents/TableOfContents.js
@@ -7,32 +7,32 @@ import filterSectionsByName from '../../utils/filterSectionsByName';
 export default class TableOfContents extends Component {
 	static propTypes = {
 		sections: PropTypes.array.isRequired,
+		useIsolatedLinks: PropTypes.bool,
 	};
 	state = {
 		searchTerm: '',
 	};
 
-	renderLevel(sections) {
+	renderLevel(sections, useIsolatedLinks = false) {
 		const items = sections.map(section => {
 			const children = [...(section.sections || []), ...(section.components || [])];
 			return Object.assign({}, section, {
 				heading: !!section.name && children.length > 0,
-				content: children.length > 0 && this.renderLevel(children),
+				content: children.length > 0 && this.renderLevel(children, useIsolatedLinks),
 			});
 		});
-		return <ComponentsList items={items} />;
+		return <ComponentsList items={items} useIsolatedLinks={useIsolatedLinks} />;
 	}
 
 	renderSections() {
 		const { searchTerm } = this.state;
-		const { sections } = this.props;
-
+		const { sections, useIsolatedLinks } = this.props;
 		// If there is only one section, we treat it as a root section
 		// In this case the name of the section won't be rendered and it won't get left padding
 		const firstLevel = sections.length === 1 ? sections[0].components : sections;
 		const filtered = filterSectionsByName(firstLevel, searchTerm);
 
-		return this.renderLevel(filtered);
+		return this.renderLevel(filtered, useIsolatedLinks);
 	}
 
 	render() {
diff --git a/src/rsg-components/TableOfContents/__snapshots__/TableOfContents.spec.js.snap b/src/rsg-components/TableOfContents/__snapshots__/TableOfContents.spec.js.snap
index fa9b98ca..027fda5d 100644
--- a/src/rsg-components/TableOfContents/__snapshots__/TableOfContents.spec.js.snap
+++ b/src/rsg-components/TableOfContents/__snapshots__/TableOfContents.spec.js.snap
@@ -19,7 +19,7 @@ exports[`should filter list when search field contains a query 1`] = `
   onSearchTermChange={[Function]}
   searchTerm=""
 >
-  <Styled(ComponentsList)
+  <ComponentsList
     items={
       Array [
         Object {
@@ -48,6 +48,7 @@ exports[`should filter list when search field contains a query 1`] = `
         },
       ]
     }
+    useIsolatedLinks={false}
   />
 </Styled(TableOfContents)>
 `;
@@ -57,7 +58,7 @@ exports[`should filter list when search field contains a query 2`] = `
   onSearchTermChange={[Function]}
   searchTerm="but"
 >
-  <Styled(ComponentsList)
+  <ComponentsList
     items={
       Array [
         Object {
@@ -70,6 +71,7 @@ exports[`should filter list when search field contains a query 2`] = `
         },
       ]
     }
+    useIsolatedLinks={false}
   />
 </Styled(TableOfContents)>
 `;
@@ -79,7 +81,7 @@ exports[`should filter section names 1`] = `
   onSearchTermChange={[Function]}
   searchTerm=""
 >
-  <Styled(ComponentsList)
+  <ComponentsList
     items={
       Array [
         Object {
@@ -97,7 +99,7 @@ exports[`should filter section names 1`] = `
               "slug": "button",
             },
           ],
-          "content": <Styled(ComponentsList)
+          "content": <ComponentsList
             items={
               Array [
                 Object {
@@ -108,6 +110,7 @@ exports[`should filter section names 1`] = `
                 },
               ]
             }
+            useIsolatedLinks={false}
           />,
           "heading": true,
           "name": "Buttons",
@@ -125,7 +128,7 @@ exports[`should filter section names 1`] = `
               "slug": "textarea",
             },
           ],
-          "content": <Styled(ComponentsList)
+          "content": <ComponentsList
             items={
               Array [
                 Object {
@@ -142,6 +145,7 @@ exports[`should filter section names 1`] = `
                 },
               ]
             }
+            useIsolatedLinks={false}
           />,
           "heading": true,
           "name": "Forms",
@@ -150,6 +154,7 @@ exports[`should filter section names 1`] = `
         },
       ]
     }
+    useIsolatedLinks={false}
   />
 </Styled(TableOfContents)>
 `;
@@ -159,7 +164,7 @@ exports[`should filter section names 2`] = `
   onSearchTermChange={[Function]}
   searchTerm="frm"
 >
-  <Styled(ComponentsList)
+  <ComponentsList
     items={
       Array [
         Object {
@@ -172,6 +177,7 @@ exports[`should filter section names 2`] = `
         },
       ]
     }
+    useIsolatedLinks={false}
   />
 </Styled(TableOfContents)>
 `;
@@ -181,7 +187,7 @@ exports[`should render a renderer 1`] = `
   onSearchTermChange={[Function]}
   searchTerm=""
 >
-  <Styled(ComponentsList)
+  <ComponentsList
     items={
       Array [
         Object {
@@ -210,6 +216,7 @@ exports[`should render a renderer 1`] = `
         },
       ]
     }
+    useIsolatedLinks={false}
   />
 </Styled(TableOfContents)>
 `;
@@ -219,7 +226,7 @@ exports[`should render renderer with sections with nested components 1`] = `
   onSearchTermChange={[Function]}
   searchTerm=""
 >
-  <Styled(ComponentsList)
+  <ComponentsList
     items={
       Array [
         Object {
@@ -237,7 +244,7 @@ exports[`should render renderer with sections with nested components 1`] = `
               "slug": "button",
             },
           ],
-          "content": <Styled(ComponentsList)
+          "content": <ComponentsList
             items={
               Array [
                 Object {
@@ -248,6 +255,7 @@ exports[`should render renderer with sections with nested components 1`] = `
                 },
               ]
             }
+            useIsolatedLinks={false}
           />,
           "heading": true,
           "name": "Buttons",
@@ -265,7 +273,7 @@ exports[`should render renderer with sections with nested components 1`] = `
               "slug": "textarea",
             },
           ],
-          "content": <Styled(ComponentsList)
+          "content": <ComponentsList
             items={
               Array [
                 Object {
@@ -282,6 +290,7 @@ exports[`should render renderer with sections with nested components 1`] = `
                 },
               ]
             }
+            useIsolatedLinks={false}
           />,
           "heading": true,
           "name": "Forms",
@@ -290,6 +299,7 @@ exports[`should render renderer with sections with nested components 1`] = `
         },
       ]
     }
+    useIsolatedLinks={false}
   />
 </Styled(TableOfContents)>
 `;
diff --git a/src/rsg-components/Usage/Usage.js b/src/rsg-components/Usage/Usage.js
index c5500de6..02641c92 100644
--- a/src/rsg-components/Usage/Usage.js
+++ b/src/rsg-components/Usage/Usage.js
@@ -21,7 +21,7 @@ export default function Usage({ props: { props, methods } }) {
 
 Usage.propTypes = {
 	props: PropTypes.shape({
-		props: PropTypes.object,
+		props: PropTypes.array,
 		methods: PropTypes.array,
 	}).isRequired,
 };
diff --git a/src/rsg-components/Usage/Usage.spec.js b/src/rsg-components/Usage/Usage.spec.js
index 5a0c5b29..ee987ea5 100644
--- a/src/rsg-components/Usage/Usage.spec.js
+++ b/src/rsg-components/Usage/Usage.spec.js
@@ -1,13 +1,14 @@
 import React from 'react';
 import Usage from './Usage';
 
-const props = {
-	children: {
+const props = [
+	{
+		name: 'children',
 		type: { name: 'string' },
 		required: true,
 		description: 'Button label.',
 	},
-};
+];
 const methods = [
 	{
 		name: 'set',
diff --git a/src/rsg-components/Usage/__snapshots__/Usage.spec.js.snap b/src/rsg-components/Usage/__snapshots__/Usage.spec.js.snap
index 55603d93..d9435adb 100644
--- a/src/rsg-components/Usage/__snapshots__/Usage.spec.js.snap
+++ b/src/rsg-components/Usage/__snapshots__/Usage.spec.js.snap
@@ -29,15 +29,16 @@ exports[`Usage should render props table 1`] = `
 <div>
   <PropsRenderer
     props={
-      Object {
-        "children": Object {
+      Array [
+        Object {
           "description": "Button label.",
+          "name": "children",
           "required": true,
           "type": Object {
             "name": "string",
           },
         },
-      }
+      ]
     }
   />
 </div>
diff --git a/src/rsg-components/Welcome/__snapshots__/Welcome.spec.js.snap b/src/rsg-components/Welcome/__snapshots__/Welcome.spec.js.snap
index 5355c4df..ae25f1a3 100644
--- a/src/rsg-components/Welcome/__snapshots__/Welcome.spec.js.snap
+++ b/src/rsg-components/Welcome/__snapshots__/Welcome.spec.js.snap
@@ -2,7 +2,7 @@
 
 exports[`renderer should render welcome screen 1`] = `
 <div>
-  <Styled(Markdown)
+  <Markdown
     text="
 # Welcome to React Styleguidist!
 
diff --git a/src/rsg-components/slots/index.js b/src/rsg-components/slots/index.js
index 838c4c68..0301f39d 100644
--- a/src/rsg-components/slots/index.js
+++ b/src/rsg-components/slots/index.js
@@ -7,34 +7,36 @@ import UsageTabButton from './UsageTabButton';
 export const EXAMPLE_TAB_CODE_EDITOR = 'rsg-code-editor';
 export const DOCS_TAB_USAGE = 'rsg-usage';
 
-const toolbar = [IsolateButton];
+export default ({ pagePerSection }) => {
+	const toolbar = pagePerSection ? [] : [IsolateButton];
 
-export default {
-	sectionToolbar: toolbar,
-	componentToolbar: toolbar,
-	exampleToolbar: toolbar,
-	exampleTabButtons: [
-		{
-			id: EXAMPLE_TAB_CODE_EDITOR,
-			render: CodeTabButton,
-		},
-	],
-	exampleTabs: [
-		{
-			id: EXAMPLE_TAB_CODE_EDITOR,
-			render: Editor,
-		},
-	],
-	docsTabButtons: [
-		{
-			id: DOCS_TAB_USAGE,
-			render: UsageTabButton,
-		},
-	],
-	docsTabs: [
-		{
-			id: DOCS_TAB_USAGE,
-			render: Usage,
-		},
-	],
+	return {
+		sectionToolbar: toolbar,
+		componentToolbar: toolbar,
+		exampleToolbar: toolbar,
+		exampleTabButtons: [
+			{
+				id: EXAMPLE_TAB_CODE_EDITOR,
+				render: CodeTabButton,
+			},
+		],
+		exampleTabs: [
+			{
+				id: EXAMPLE_TAB_CODE_EDITOR,
+				render: Editor,
+			},
+		],
+		docsTabButtons: [
+			{
+				id: DOCS_TAB_USAGE,
+				render: UsageTabButton,
+			},
+		],
+		docsTabs: [
+			{
+				id: DOCS_TAB_USAGE,
+				render: Usage,
+			},
+		],
+	};
 };
diff --git a/src/rsg-components/slots/index.spec.js b/src/rsg-components/slots/index.spec.js
new file mode 100644
index 00000000..eda6fe19
--- /dev/null
+++ b/src/rsg-components/slots/index.spec.js
@@ -0,0 +1,18 @@
+import slots from './index';
+import IsolateButton from './IsolateButton';
+
+describe('slots', () => {
+	it('should contain IsolateButton in toolbars if pagePerSection is false', () => {
+		const config = slots({ pagePerSection: false });
+		expect(config.componentToolbar).toContain(IsolateButton);
+		expect(config.exampleToolbar).toContain(IsolateButton);
+		expect(config.sectionToolbar).toContain(IsolateButton);
+	});
+
+	it('should not contain IsolateButton in toolbars if pagePerSection is true', () => {
+		const config = slots({ pagePerSection: true });
+		expect(config.componentToolbar).not.toContain(IsolateButton);
+		expect(config.exampleToolbar).not.toContain(IsolateButton);
+		expect(config.sectionToolbar).not.toContain(IsolateButton);
+	});
+});
diff --git a/src/utils/getComponent.js b/src/utils/getComponent.js
new file mode 100644
index 00000000..172ada01
--- /dev/null
+++ b/src/utils/getComponent.js
@@ -0,0 +1,60 @@
+/**
+ * Given a component module and a name,
+ * return the appropriate export.
+ * See /docs/Components.md
+ *
+ * @param  {object} module
+ * @param  {string} name
+ * @return {function|object}
+ */
+export default function getComponent(module, name) {
+	//
+	// If the module defines a default export, return that
+	// e.g.
+	//
+	// ```
+	// export default function Component() { ... }
+	// ```
+	//
+	if (module.default) {
+		return module.default;
+	}
+
+	// If it is a CommonJS module which exports a function, return that
+	// e.g.
+	//
+	// ```
+	// function Component() { ... }
+	// module.exports = Component;
+	// ```
+	//
+	if (!module.__esModule && typeof module === 'function') {
+		return module;
+	}
+	const moduleKeys = Object.keys(module);
+
+	// If the module exports just one named export, return that
+	// e.g.
+	//
+	// ```
+	// export function Component() { ... }
+	// ```
+	//
+	if (moduleKeys.length === 1) {
+		return module[moduleKeys[0]];
+	}
+
+	// If the module exports a named export with the same name as the
+	// understood Component identifier, return that
+	// e.g.
+	//
+	// ```
+	// // /component.js
+	// export function someUtil() { ... }
+	// export Component() { ... } // if identifier is Component, return this named export
+	// ```
+	//
+	// Else return the module itself
+	//
+	return module[name] || module;
+}
diff --git a/src/utils/getRouteData.js b/src/utils/getRouteData.js
index 179141ab..23d8276f 100644
--- a/src/utils/getRouteData.js
+++ b/src/utils/getRouteData.js
@@ -6,6 +6,21 @@ import findSection from './findSection';
 import getInfoFromHash from './getInfoFromHash';
 import { DisplayModes } from '../consts';
 
+/**
+ * Returns the first element of the sections, which can be one of section or
+ * component.
+ *
+ * @param {object} sections
+ * @returns {object}
+ */
+const getFirstSectionOrComponent = sections => {
+	const firstSection = sections[0];
+	if (firstSection.components && firstSection.components.length > 0) {
+		return { ...firstSection, components: [firstSection.components[0]] };
+	}
+	return firstSection;
+};
+
 /**
  * Return sections / components / examples to show on a screen according to a current route.
  *
@@ -15,9 +30,10 @@ import { DisplayModes } from '../consts';
  *
  * @param {object} sections
  * @param {string} hash
+ * @param {boolean} pagePerSection
  * @returns {object}
  */
-export default function getRouteData(sections, hash) {
+export default function getRouteData(sections, hash, pagePerSection) {
 	// Parse URL hash to check if the components list must be filtered
 	const {
 		// Name of the filtered component/section to show isolated (/#!/Button → Button)
@@ -50,6 +66,9 @@ export default function getRouteData(sections, hash) {
 				displayMode = DisplayModes.example;
 			}
 		}
+	} else if (pagePerSection) {
+		// If one component per page mode then show demos for first component
+		sections = [getFirstSectionOrComponent(sections)];
 	}
 
 	return { sections, displayMode };
diff --git a/src/utils/globalizeComponent.js b/src/utils/globalizeComponent.js
index 2c7244b5..ba00a03f 100644
--- a/src/utils/globalizeComponent.js
+++ b/src/utils/globalizeComponent.js
@@ -1,3 +1,5 @@
+import getComponent from './getComponent';
+
 /**
  * Expose component as global variables.
  *
@@ -8,8 +10,5 @@ export default function globalizeComponent(component) {
 		return;
 	}
 
-	global[component.name] =
-		!component.props.path || component.props.path === 'default'
-			? component.module.default || component.module
-			: component.module[component.props.path];
+	global[component.name] = getComponent(component.module, component.name);
 }
diff --git a/src/utils/renderStyleguide.js b/src/utils/renderStyleguide.js
index f9f67494..18d4c544 100644
--- a/src/utils/renderStyleguide.js
+++ b/src/utils/renderStyleguide.js
@@ -27,10 +27,11 @@ export default function renderStyleguide(
 	// all components accessible to all examples
 	globalizeComponents(allSections);
 
-	const { sections, displayMode } = getRouteData(allSections, loc.hash);
+	const { title, pagePerSection } = styleguide.config;
+	const { sections, displayMode } = getRouteData(allSections, loc.hash, pagePerSection);
 
 	// Update page title
-	doc.title = getPageTitle(sections, styleguide.config.title, displayMode);
+	doc.title = getPageTitle(sections, title, displayMode);
 
 	// If the current hash location was set to just `/` (e.g. when navigating back from isolated view to overview)
 	// replace the URL with one without hash, to present the user with a single address of the overview screen
@@ -43,11 +44,13 @@ export default function renderStyleguide(
 		<StyleGuide
 			codeRevision={codeRevision}
 			config={styleguide.config}
-			slots={slots}
+			slots={slots(styleguide.config)}
 			welcomeScreen={styleguide.welcomeScreen}
 			patterns={styleguide.patterns}
 			sections={sections}
+			allSections={allSections}
 			displayMode={displayMode}
+			pagePerSection={pagePerSection}
 		/>
 	);
 }


Test patch:
diff --git a/loaders/__tests__/props-loader.spec.js b/loaders/__tests__/props-loader.spec.js
index 3232e05e..41a22497 100644
--- a/loaders/__tests__/props-loader.spec.js
+++ b/loaders/__tests__/props-loader.spec.js
@@ -1,6 +1,8 @@
 import vm from 'vm';
 import { readFileSync } from 'fs';
 import glogg from 'glogg';
+
+import sortBy from 'lodash/sortBy';
 import config from '../../scripts/schemas/config';
 import propsLoader from '../props-loader';
 
@@ -44,6 +46,67 @@ it('should extract doclets', () => {
 	expect(result).toMatch(/require\('!!.*?\/loaders\/examples-loader\.js!\.\/examples.md'\)/);
 });
 
+describe('property sorting', () => {
+	it('should sort properties by default', () => {
+		const file = './test/components/Price/Price.js';
+		const result = propsLoader.call(
+			{
+				request: file,
+				_styleguidist,
+			},
+			readFileSync(file, 'utf8')
+		);
+		expect(result).toBeTruthy();
+
+		expect(() => new vm.Script(result)).not.toThrow();
+		expect(result.includes('makeABarrelRoll')).toBe(false);
+		expect(result).toMatch(
+			/props[\s\S]*?name': 'symbol'[\s\S]*?name': 'value'[\s\S]*?name': 'emphasize'[\s\S]*?name': 'unit'/m
+		);
+	});
+
+	it('should be possible to disable sorting', () => {
+		const file = './test/components/Price/Price.js';
+		const result = propsLoader.call(
+			{
+				request: file,
+				_styleguidist: { ..._styleguidist, propsTransform: props => props },
+			},
+			readFileSync(file, 'utf8')
+		);
+		expect(result).toBeTruthy();
+
+		expect(() => new vm.Script(result)).not.toThrow();
+		expect(result.includes('makeABarrelRoll')).toBe(false);
+		expect(result).toMatch(
+			/props[\s\S]*?name': 'value'[\s\S]*?name': 'unit'[\s\S]*?name': 'emphasize'[\s\S]*?name': 'symbol'/m
+		);
+	});
+
+	it('should be possible to write custom sort function', () => {
+		const sortFn = props => {
+			const requiredProps = sortBy(props.filter(prop => prop.required), 'name').reverse();
+			const optionalProps = sortBy(props.filter(prop => !prop.required), 'name').reverse();
+			return optionalProps.concat(requiredProps);
+		};
+		const file = './test/components/Price/Price.js';
+		const result = propsLoader.call(
+			{
+				request: file,
+				_styleguidist: { ..._styleguidist, propsTransform: sortFn },
+			},
+			readFileSync(file, 'utf8')
+		);
+		expect(result).toBeTruthy();
+
+		expect(() => new vm.Script(result)).not.toThrow();
+		expect(result.includes('makeABarrelRoll')).toBe(false);
+		expect(result).toMatch(
+			/props[\s\S]*?name': 'unit'[\s\S]*?name': 'emphasize'[\s\S]*?name': 'value'[\s\S]*?name': 'symbol'/m
+		);
+	});
+});
+
 it('should work with JSDoc annnotated components', () => {
 	const file = './test/components/Annotation/Annotation.js';
 	const result = propsLoader.call(
diff --git a/loaders/utils/__tests__/__snapshots__/getSections.spec.js.snap b/loaders/utils/__tests__/__snapshots__/getSections.spec.js.snap
index 57e8da85..6af4869a 100644
--- a/loaders/utils/__tests__/__snapshots__/getSections.spec.js.snap
+++ b/loaders/utils/__tests__/__snapshots__/getSections.spec.js.snap
@@ -56,6 +56,19 @@ Array [
         },
         "slug": "placeholder-2",
       },
+      Object {
+        "filepath": "components/Price/Price.js",
+        "hasExamples": true,
+        "metadata": Object {},
+        "module": Object {
+          "require": "~/test/components/Price/Price.js",
+        },
+        "pathLine": "components/Price/Price.js",
+        "props": Object {
+          "require": "!!~/loaders/props-loader.js!~/test/components/Price/Price.js",
+        },
+        "slug": "price-2",
+      },
       Object {
         "filepath": "components/RandomButton/RandomButton.js",
         "hasExamples": true,
@@ -107,6 +120,19 @@ Array [
         },
         "slug": "placeholder-3",
       },
+      Object {
+        "filepath": "components/Price/Price.js",
+        "hasExamples": true,
+        "metadata": Object {},
+        "module": Object {
+          "require": "~/test/components/Price/Price.js",
+        },
+        "pathLine": "components/Price/Price.js",
+        "props": Object {
+          "require": "!!~/loaders/props-loader.js!~/test/components/Price/Price.js",
+        },
+        "slug": "price-3",
+      },
       Object {
         "filepath": "components/RandomButton/RandomButton.js",
         "hasExamples": true,
@@ -175,6 +201,19 @@ Object {
       },
       "slug": "placeholder",
     },
+    Object {
+      "filepath": "components/Price/Price.js",
+      "hasExamples": true,
+      "metadata": Object {},
+      "module": Object {
+        "require": "~/test/components/Price/Price.js",
+      },
+      "pathLine": "components/Price/Price.js",
+      "props": Object {
+        "require": "!!~/loaders/props-loader.js!~/test/components/Price/Price.js",
+      },
+      "slug": "price",
+    },
     Object {
       "filepath": "components/RandomButton/RandomButton.js",
       "hasExamples": true,
@@ -243,6 +282,19 @@ Object {
       },
       "slug": "placeholder-1",
     },
+    Object {
+      "filepath": "components/Price/Price.js",
+      "hasExamples": true,
+      "metadata": Object {},
+      "module": Object {
+        "require": "~/test/components/Price/Price.js",
+      },
+      "pathLine": "components/Price/Price.js",
+      "props": Object {
+        "require": "!!~/loaders/props-loader.js!~/test/components/Price/Price.js",
+      },
+      "slug": "price-1",
+    },
     Object {
       "filepath": "components/RandomButton/RandomButton.js",
       "hasExamples": true,
diff --git a/loaders/utils/__tests__/getComponentFiles.spec.js b/loaders/utils/__tests__/getComponentFiles.spec.js
index 97b0ed1f..91f2ba4c 100644
--- a/loaders/utils/__tests__/getComponentFiles.spec.js
+++ b/loaders/utils/__tests__/getComponentFiles.spec.js
@@ -24,12 +24,24 @@ it('getComponentFiles() should accept components as a function that returns abso
 	expect(deabs(result)).toEqual(['~/one.js', '~/two.js']);
 });
 
+it('getComponentFiles() should accept components as an array of file names', () => {
+	const result = getComponentFiles(components, configDir);
+	expect(deabs(result)).toEqual(['~/one.js', '~/two.js']);
+});
+
+it('getComponentFiles() should accept components as a function that returns absolute paths', () => {
+	const absolutize = files => files.map(file => path.join(configDir, file));
+	const result = getComponentFiles(absolutize(components), configDir);
+	expect(deabs(result)).toEqual(['~/one.js', '~/two.js']);
+});
+
 it('getComponentFiles() should accept components as a glob', () => {
 	const result = getComponentFiles(glob, configDir);
 	expect(deabs(result)).toEqual([
 		'~/components/Annotation/Annotation.js',
 		'~/components/Button/Button.js',
 		'~/components/Placeholder/Placeholder.js',
+		'~/components/Price/Price.js',
 		'~/components/RandomButton/RandomButton.js',
 	]);
 });
@@ -39,10 +51,11 @@ it('getComponentFiles() should ignore specified patterns', () => {
 	expect(deabs(result)).toEqual([
 		'~/components/Annotation/Annotation.js',
 		'~/components/Placeholder/Placeholder.js',
+		'~/components/Price/Price.js',
 	]);
 });
 
-it('getComponentFiles() should throw if components is not a function or a string', () => {
+it('getComponentFiles() should throw if components is not a function, array or a string', () => {
 	const fn = () => getComponentFiles(42, configDir);
-	expect(fn).toThrowError('should be string or function');
+	expect(fn).toThrowError('should be string, function or array');
 });
diff --git a/loaders/utils/__tests__/getComponentFilesFromSections.spec.js b/loaders/utils/__tests__/getComponentFilesFromSections.spec.js
index 54da82b0..9127315d 100644
--- a/loaders/utils/__tests__/getComponentFilesFromSections.spec.js
+++ b/loaders/utils/__tests__/getComponentFilesFromSections.spec.js
@@ -30,10 +30,14 @@ it('getComponentFilesFromSections() should return a list of files', () => {
 	expect(deabs(result)).toEqual([
 		'~/components/Button/Button.js',
 		'~/components/Placeholder/Placeholder.js',
+		'~/components/Price/Price.js',
 	]);
 });
 
 it('getComponentFilesFromSections() should ignore specified patterns', () => {
 	const result = getComponentFilesFromSections(sections, configDir, ['**/*Button*']);
-	expect(deabs(result)).toEqual(['~/components/Placeholder/Placeholder.js']);
+	expect(deabs(result)).toEqual([
+		'~/components/Placeholder/Placeholder.js',
+		'~/components/Price/Price.js',
+	]);
 });
diff --git a/loaders/utils/__tests__/sortProps.spec.js b/loaders/utils/__tests__/sortProps.spec.js
new file mode 100644
index 00000000..a3d99662
--- /dev/null
+++ b/loaders/utils/__tests__/sortProps.spec.js
@@ -0,0 +1,37 @@
+import sortProps from '../sortProps';
+
+function makeProp(name, required, defaultValue, type) {
+	// Default values.
+	required = required === undefined ? false : required;
+	type = type === undefined ? { name: 'string' } : type;
+
+	return {
+		name,
+		required,
+		defaultValue,
+		type,
+	};
+}
+
+it('should sort required props', () => {
+	const props = [makeProp('prop2', true), makeProp('prop1', true)];
+	const result = sortProps(props);
+	expect(result.map(prop => prop.name)).toEqual(['prop1', 'prop2']);
+});
+
+it('should sort optional props', () => {
+	const props = [makeProp('prop2', false), makeProp('prop1', false)];
+	const result = sortProps(props);
+	expect(result.map(prop => prop.name)).toEqual(['prop1', 'prop2']);
+});
+
+it('should sort mixed props (required props should come first)', () => {
+	const props = [
+		makeProp('prop2', false),
+		makeProp('prop1', true),
+		makeProp('prop3', true),
+		makeProp('prop4', false),
+	];
+	const result = sortProps(props);
+	expect(result.map(prop => prop.name)).toEqual(['prop1', 'prop3', 'prop2', 'prop4']);
+});
diff --git a/src/utils/__tests__/__snapshots__/getRouteData.spec.js.snap b/src/utils/__tests__/__snapshots__/getRouteData.spec.js.snap
index a4df1d18..9f866ecf 100644
--- a/src/utils/__tests__/__snapshots__/getRouteData.spec.js.snap
+++ b/src/utils/__tests__/__snapshots__/getRouteData.spec.js.snap
@@ -1,5 +1,73 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`getRouteData should return first component if pagePerSection and hash is empty 1`] = `
+Object {
+  "displayMode": "all",
+  "sections": Array [
+    Object {
+      "components": Array [
+        Object {
+          "module": 1,
+          "name": "Button",
+          "props": Object {
+            "displayName": "Button",
+            "examples": Array [
+              "example 0",
+              "example 1",
+            ],
+          },
+        },
+      ],
+      "sections": Array [],
+    },
+  ],
+}
+`;
+
+exports[`getRouteData should return first section if pagePerSection and hash is empty 1`] = `
+Object {
+  "displayMode": "all",
+  "sections": Array [
+    Object {
+      "sections": Array [
+        Object {
+          "components": Array [
+            Object {
+              "module": 1,
+              "name": "Button",
+              "props": Object {
+                "displayName": "Button",
+                "examples": Array [
+                  "example 0",
+                  "example 1",
+                ],
+              },
+            },
+            Object {
+              "module": 2,
+              "name": "Image",
+              "props": Object {
+                "displayName": "Image",
+              },
+            },
+          ],
+          "sections": Array [],
+        },
+        Object {
+          "components": Array [],
+          "content": Array [
+            "example 0",
+            "example 1",
+          ],
+          "name": "Section",
+          "sections": Array [],
+        },
+      ],
+    },
+  ],
+}
+`;
+
 exports[`getRouteData should return one component 1`] = `
 Object {
   "displayMode": "component",
diff --git a/src/utils/__tests__/__snapshots__/renderStyleguide.spec.js.snap b/src/utils/__tests__/__snapshots__/renderStyleguide.spec.js.snap
index d0ead0d4..a2416be1 100644
--- a/src/utils/__tests__/__snapshots__/renderStyleguide.spec.js.snap
+++ b/src/utils/__tests__/__snapshots__/renderStyleguide.spec.js.snap
@@ -31,6 +31,7 @@ exports[`renderStyleguide should render StyleGuide component 1`] = `
           },
         ]
       }
+      useIsolatedLinks={undefined}
     />
   }
 >
diff --git a/src/utils/__tests__/getComponent.spec.js b/src/utils/__tests__/getComponent.spec.js
new file mode 100644
index 00000000..ed038099
--- /dev/null
+++ b/src/utils/__tests__/getComponent.spec.js
@@ -0,0 +1,47 @@
+import getComponent from '../getComponent';
+
+describe('getComponent', () => {
+	describe('if there is a default export in the module', () => {
+		it('should return that', () => {
+			const module = { default: 'useMe' };
+			const actual = getComponent(module);
+			expect(actual).toBe(module.default);
+		});
+	});
+
+	describe('if it is a CommonJS module and exports a function', () => {
+		it('should return the module', () => {
+			const testCases = [() => {}, function() {}, class Class {}];
+			testCases.forEach(testCase => {
+				const actual = getComponent(testCase);
+				expect(actual).toBe(testCase);
+			});
+		});
+	});
+
+	describe('if there is only one named export in the module', () => {
+		it('should return that', () => {
+			const module = { oneNamedExport: 'isLonely' };
+			const actual = getComponent(module);
+			expect(actual).toBe(module.oneNamedExport);
+		});
+	});
+
+	describe('if there is a named export whose name matches the name argument', () => {
+		it('should return that', () => {
+			const name = 'Component';
+			const module = { [name]: 'isNamed', OtherComponent: 'isAlsoNamed' };
+			const actual = getComponent(module, name);
+			expect(actual).toBe(module[name]);
+		});
+	});
+
+	describe('if there is more than one named export and no matching name', () => {
+		it('should fall back on returning the module as a whole', () => {
+			const name = 'Component';
+			const module = { RandomName: 'isNamed', confusingExport: 123 };
+			const actual = getComponent(module, name);
+			expect(actual).toBe(module);
+		});
+	});
+});
diff --git a/src/utils/__tests__/getRouteData.spec.js b/src/utils/__tests__/getRouteData.spec.js
index b7fce1d7..7f37694b 100644
--- a/src/utils/__tests__/getRouteData.spec.js
+++ b/src/utils/__tests__/getRouteData.spec.js
@@ -60,4 +60,14 @@ describe('getRouteData', () => {
 		const result = getRouteData(sections, '#!/Section/1');
 		expect(result).toMatchSnapshot();
 	});
+
+	it('should return first section if pagePerSection and hash is empty', () => {
+		const result = getRouteData(sections, '', true);
+		expect(result).toMatchSnapshot();
+	});
+
+	it('should return first component if pagePerSection and hash is empty', () => {
+		const result = getRouteData(sections[0].sections, '', true);
+		expect(result).toMatchSnapshot();
+	});
 });
diff --git a/src/utils/__tests__/globalizeComponent.spec.js b/src/utils/__tests__/globalizeComponent.spec.js
index 78fb384a..6c4079fa 100644
--- a/src/utils/__tests__/globalizeComponent.spec.js
+++ b/src/utils/__tests__/globalizeComponent.spec.js
@@ -1,18 +1,21 @@
 import globalizeComponent from '../globalizeComponent';
 
-afterEach(() => {
-	delete global.Foo;
-});
+const component = { module: 'someModule', name: 'SomeName' };
 
 describe('globalizeComponent', () => {
-	it('should set component’s module as a global variable', () => {
-		const globalsCount = Object.keys(global).length;
-		globalizeComponent({
-			name: 'Foo',
-			props: {},
-			module: 13,
-		});
-		expect(Object.keys(global).length).toBe(globalsCount + 1);
-		expect(global.Foo).toBe(13);
+	afterEach(() => {
+		delete global[component.name];
+	});
+
+	it('should not add anything as a global variable if there is no component name', () => {
+		expect(global[component.name]).toBeUndefined();
+		globalizeComponent({});
+		expect(global[component.name]).toBeUndefined();
+	});
+
+	it('should set the return value of getComponent as a global variable', () => {
+		expect(global[component.name]).toBeUndefined();
+		globalizeComponent(component);
+		expect(global[component.name]).toBe(component.module);
 	});
 });
diff --git a/test/components/Price/Price.js b/test/components/Price/Price.js
new file mode 100644
index 00000000..aa9abb9b
--- /dev/null
+++ b/test/components/Price/Price.js
@@ -0,0 +1,34 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+const unitSymbols = {
+	USD: '$',
+	EUR: '€',
+};
+
+/**
+ * Price component that renders a price and a unit.
+ */
+export default function Price(props) {
+	let Host = 'span';
+	if (props.emphasize) {
+		Host = 'em';
+	}
+	return (
+		<Host>
+			{props.value}
+			{!props.symbol ? props.unit : unitSymbols[props.unit]}
+		</Host>
+	);
+}
+
+Price.propTypes = {
+	/** Price value. */
+	value: PropTypes.number.isRequired,
+	/** Price unit */
+	unit: PropTypes.oneOf(['EUR', 'USD']),
+	/** Flag that determines if the price should be emphasized or not. */
+	emphasize: PropTypes.bool,
+	/** Defines if the unit should be shown as a symbol or not. */
+	symbol: PropTypes.bool.isRequired,
+};
diff --git a/cypress/fixtures/example.json b/test/cypress/fixtures/example.json
similarity index 100%
rename from cypress/fixtures/example.json
rename to test/cypress/fixtures/example.json
diff --git a/cypress/integration/component_spec.js b/test/cypress/integration/component_spec.js
similarity index 100%
rename from cypress/integration/component_spec.js
rename to test/cypress/integration/component_spec.js
diff --git a/cypress/integration/core_spec.js b/test/cypress/integration/core_spec.js
similarity index 100%
rename from cypress/integration/core_spec.js
rename to test/cypress/integration/core_spec.js
diff --git a/cypress/plugins/index.js b/test/cypress/plugins/index.js
similarity index 100%
rename from cypress/plugins/index.js
rename to test/cypress/plugins/index.js
diff --git a/cypress/support/commands.js b/test/cypress/support/commands.js
similarity index 100%
rename from cypress/support/commands.js
rename to test/cypress/support/commands.js
diff --git a/cypress/support/index.js b/test/cypress/support/index.js
similarity index 100%
rename from cypress/support/index.js
rename to test/cypress/support/index.js
diff --git a/test/empty.js b/test/empty.js
new file mode 100644
index 00000000..9dc5fc1e
--- /dev/null
+++ b/test/empty.js
@@ -0,0 +1,1 @@
+module.exports = '';