Repository: styleguidist/react-styleguidist
Pull request #993 (2018-05-25 21:00:57)

Feat: Page per section upgraded with page not found

This PR is a feature of this issue:
https://github.com/styleguidist/react-styleguidist/issues/892

And started with this branch
https://github.com/styleguidist/react-styleguidist/pull/989

Also added a page not found
![image](https://user-images.githubusercontent.com/10201025/40565894-ecbd4c5e-6033-11e8-90a3-eef5eee6aa2f.png)




Patch:
diff --git a/docs/Components.md b/docs/Components.md
index ecb282f8..306f1bef 100644
--- a/docs/Components.md
+++ b/docs/Components.md
@@ -109,6 +109,9 @@ Each section consists of (all fields are optional):
 * `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.
+* `sectionDepth` — Number of subsections with single pages, only available with [pagePerSection](Configuration.md#pagepersection) is enabled.
+* `exampleMode` — Initial state of the code example tab, uses [exampleMode](Configuration.md#examplemode).
+* `usageMode` — Initial state of the props and methods tab, uses [usageMode](Configuration.md#usagemode).
 * `ignore` — string/array of globs that should not be included in the section.
 
 Configuring a style guide with textual documentation section and a list of components would look like:
@@ -137,7 +140,9 @@ module.exports = {
     {
       name: 'UI Components',
       content: 'docs/ui.md',
-      components: 'lib/components/ui/*.js'
+      components: 'lib/components/ui/*.js',
+      exampleMode: 'expand', // 'hide' | 'collapse' | 'expand'
+      usageMode: 'expand' // 'hide' | 'collapse' | 'expand'
     }
   ]
 }
diff --git a/docs/Configuration.md b/docs/Configuration.md
index 28193e32..8f27c8a0 100644
--- a/docs/Configuration.md
+++ b/docs/Configuration.md
@@ -15,6 +15,7 @@ By default, Styleguidist will look for `styleguide.config.js` file in your proje
 * [`dangerouslyUpdateWebpackConfig`](#dangerouslyupdatewebpackconfig)
 * [`defaultExample`](#defaultexample)
 * [`editorConfig`](#editorconfig)
+* [`exampleMode`](#examplemode)
 * [`getComponentPathLine`](#getcomponentpathline)
 * [`getExampleFilename`](#getexamplefilename)
 * [`handlers`](#handlers)
@@ -31,8 +32,6 @@ By default, Styleguidist will look for `styleguide.config.js` file in your proje
 * [`sections`](#sections)
 * [`serverHost`](#serverhost)
 * [`serverPort`](#serverport)
-* [`showCode`](#showcode)
-* [`showUsage`](#showusage)
 * [`showSidebar`](#showsidebar)
 * [`skipComponentsWithoutExample`](#skipcomponentswithoutexample)
 * [`sortProps`](#sortprops)
@@ -44,6 +43,7 @@ By default, Styleguidist will look for `styleguide.config.js` file in your proje
 * [`title`](#title)
 * [`updateDocs`](#updatedocs)
 * [`updateExample`](#updateexample)
+* [`usageMode`](#usagemode)
 * [`verbose`](#verbose)
 * [`webpackConfig`](#webpackconfig)
 
@@ -197,6 +197,16 @@ module.exports = {
 }
 ```
 
+#### `exampleMode`
+
+Type: `String`, default: `collapse`
+
+Defines the initial state of the example code tab:
+
+* `collapse`: collapses the tab by default.
+* `hide`: hide the tab and it can´t be toggled in the UI.
+* `expand`: expand the tab by default.
+
 #### `handlers`
 
 Type: `Function`, optional, default: [[react-docgen-displayname-handler](https://github.com/nerdlabs/react-docgen-displayname-handler)]
@@ -272,7 +282,7 @@ 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.
+If set to `true`, each section will be single page.
 
 The value may be differ on each environment.
 
@@ -282,6 +292,63 @@ module.exports = {
 }
 ```
 
+If you want to isolate section's children as single pages (sub-routes), you can add `sectionDepth` in each section, which it is the number sub-routes for depth that it will have.
+
+For example:
+
+```javascript
+module.exports = {
+  pagePerSection: true,
+  sections: [
+    {
+      name: 'Documentation',
+      sections: [
+        {
+          name: 'Files',
+          sections: [
+            {
+              name: 'First File'
+            },
+            {
+              name: 'Second File'
+            }
+          ]
+        }
+      ],
+      sectionDepth: 2 // It will show "Documentation" and "Files" as single pages, filtering his children.
+    },
+    {
+      name: 'Components',
+      sections: [
+        {
+          name: 'Buttons',
+          sections: [
+            {
+              name: 'WrapperButton'
+            }
+          ]
+        }
+      ]
+			sectionDepth: 1, // It will show "Components" as single page, filtering his children.
+    },
+    {
+      name: 'Examples',
+      sections: [
+        {
+          name: 'Case 1',
+          sections: [
+            {
+              name: 'Buttons'
+            }
+          ]
+        }
+      ]
+			sectionDepth: 0, // There is not sub-routes, "Examples" will be show all his children on page.
+    }
+  ]
+}
+```
+
 #### `printBuildInstructions`
 
 Type: `Function`, optional
@@ -419,18 +486,6 @@ Type: `Number`, default: `6060`
 
 Dev server port.
 
-#### `showCode`
-
-Type: `Boolean`, default: `false`
-
-Show or hide example code initially. It can be toggled in the UI by clicking the Code button after each example.
-
-#### `showUsage`
-
-Type: `Boolean`, default: `false`
-
-Show or hide props and methods documentation initially. It can be toggled in the UI by clicking the Props & methods button after each component description.
-
 #### `showSidebar`
 
 Type: `Boolean`, default: `true`
@@ -590,6 +645,16 @@ module.exports = {
 }
 ```
 
+#### `usageMode`
+
+Type: `String`, default: `collapse`
+
+Defines the initial state of the props and methods tab:
+
+* `collapse`: collapses the tab by default.
+* `hide`: hide the tab and it can´t be toggled in the UI.
+* `expand`: expand the tab by default.
+
 Use it like this in your Markdown files:
 
     ```js { "file": "./some/file.js" }
diff --git a/examples/sections/docs/Components.md b/examples/sections/docs/Components.md
new file mode 100644
index 00000000..0e368cf5
--- /dev/null
+++ b/examples/sections/docs/Components.md
@@ -0,0 +1,9 @@
+Tempor qui ad ad sint nulla sint magna aliquip qui ex. Non commodo mollit et exercitation nostrud esse. Minim aliqua cillum est amet dolore ipsum qui tempor eu ea.
+
+Ut veniam sit pariatur deserunt non officia. Esse commodo proident quis culpa esse enim occaecat occaecat laborum nostrud non non sunt. Labore incididunt reprehenderit sunt elit reprehenderit nulla nulla Lorem aliquip incididunt. Qui cillum consectetur Lorem anim amet ex magna deserunt sunt.
+
+List of components:
+
+* [Buttons](#/Components?id=buttons)
+* [Fields](#/Components?id=fields)
+* [Others](#/Components?id=others)
diff --git a/examples/sections/docs/Documentation.md b/examples/sections/docs/Documentation.md
new file mode 100644
index 00000000..7f083a45
--- /dev/null
+++ b/examples/sections/docs/Documentation.md
@@ -0,0 +1,5 @@
+Excepteur ullamco ut ea laborum in duis fugiat in dolor minim non. Minim amet velit duis fugiat nisi excepteur occaecat elit exercitation eiusmod est aute nulla. Et deserunt labore consectetur elit nostrud consequat commodo occaecat consectetur mollit sunt. Eiusmod laboris incididunt consectetur nisi nulla cupidatat. Velit consequat voluptate eiusmod aliqua esse culpa tempor quis tempor dolor dolor irure.
+
+Proident laboris esse do culpa ullamco proident deserunt minim nostrud et ut deserunt. Pariatur cillum et nulla labore proident nisi eu veniam. Nulla aute non incididunt laboris dolor magna laborum eu incididunt est.
+
+Ut culpa non sint eiusmod ut duis. Cillum consequat labore cupidatat eiusmod aute aute. Minim irure labore ea cillum id consectetur incididunt ex tempor deserunt labore. Officia deserunt consequat cupidatat nulla quis aute. Enim laborum aliquip aliquip sit exercitation sint eiusmod do do. Irure esse aliqua veniam do laborum cillum dolor nulla laborum consequat velit amet. Mollit ullamco incididunt Lorem dolor ut qui sunt proident.
diff --git a/examples/sections/docs/Files.md b/examples/sections/docs/Files.md
new file mode 100644
index 00000000..8c9c371b
--- /dev/null
+++ b/examples/sections/docs/Files.md
@@ -0,0 +1,9 @@
+Qui adipisicing officia voluptate cillum duis magna eu esse. Officia velit do tempor veniam exercitation ullamco irure. Reprehenderit laborum aliquip ipsum dolore ullamco. Consequat irure commodo culpa id nostrud. Aute et officia esse cillum amet commodo consequat nulla amet aute sint elit proident. Incididunt sunt tempor in ullamco ea eiusmod quis mollit occaecat ea. Sint minim eu mollit occaecat cupidatat velit sunt aliqua reprehenderit do minim anim magna dolore.
+
+Aute fugiat nulla eu cillum dolore proident eu nisi sint minim veniam. Sunt velit aliqua ullamco quis qui sint aliqua incididunt irure dolor sint nulla commodo qui. Eu nisi pariatur Lorem dolore id aute occaecat adipisicing. Consectetur cupidatat magna magna aute anim eu non officia Lorem esse. Pariatur exercitation id velit anim id laborum dolore laboris esse est in anim dolor. Aute ea in et laboris culpa voluptate laboris ipsum nisi.
+
+List of files:
+
+* [First File](#/Documentation/Files/First%20File)
+* [Second File](#/Documentation/Files/Second%20File)
+* [WrappedButton](#/Documentation/Files/WrappedButton)
diff --git a/examples/sections/styleguide.config.js b/examples/sections/styleguide.config.js
index af543b5a..0e776ad4 100644
--- a/examples/sections/styleguide.config.js
+++ b/examples/sections/styleguide.config.js
@@ -6,14 +6,18 @@ module.exports = {
 	sections: [
 		{
 			name: 'Documentation',
+			content: 'docs/Documentation.md',
 			sections: [
 				{
 					name: 'Files',
+					content: 'docs/Files.md',
+					components: () => ['./src/components/WrappedButton/WrappedButton.js'],
 					sections: [
 						{
 							name: 'First File',
 							content: 'docs/One.md',
 							description: 'This is the first section description',
+							components: () => ['./src/components/Label/Label.js'],
 						},
 						{
 							name: 'Second File',
@@ -22,26 +26,32 @@ module.exports = {
 					],
 				},
 			],
+			sectionDepth: 2,
 		},
 		{
 			name: 'Components',
+			content: 'docs/Components.md',
 			sections: [
 				{
 					name: 'Buttons',
-					components: () => [
-						'./src/components/Button/Button.js',
-						'./src/components/RandomButton/RandomButton.js',
-						'./src/components/WrappedButton/WrappedButton.js',
-					],
+					components: () => ['./src/components/Button/Button.js'],
+					exampleMode: 'expand', // 'hide' | 'collapse' | 'expand'
+					usageMode: 'hide', // 'hide' | 'collapse' | 'expand'
 				},
 				{
 					name: 'Fields',
-					components: () => [
-						'./src/components/Label/Label.js',
-						'./src/components/Placeholder/Placeholder.js',
-					],
+					components: () => ['./src/components/Placeholder/Placeholder.js'],
+					exampleMode: 'expand', // 'hide' | 'collapse' | 'expand'
+					usageMode: 'expand', // 'hide' | 'collapse' | 'expand'
+				},
+				{
+					name: 'Others',
+					components: () => ['./src/components/RandomButton/RandomButton.js'],
+					exampleMode: 'collapse', // 'hide' | 'collapse' | 'expand'
+					usageMode: 'collapse', // 'hide' | 'collapse' | 'expand'
 				},
 			],
+			sectionDepth: 0,
 		},
 	],
 	require: [path.join(__dirname, 'src/styles.css')],
diff --git a/loaders/utils/getSections.js b/loaders/utils/getSections.js
index ab8f6577..22a4b772 100644
--- a/loaders/utils/getSections.js
+++ b/loaders/utils/getSections.js
@@ -15,10 +15,11 @@ const examplesLoader = path.resolve(__dirname, '../examples-loader.js');
  *
  * @param {Array} sections
  * @param {object} config
+ * @param {number} parentDepth
  * @returns {Array}
  */
-function getSections(sections, config) {
-	return sections.map(section => processSection(section, config));
+function getSections(sections, config, parentDepth) {
+	return sections.map(section => processSection(section, config, parentDepth));
 }
 
 const getSectionComponents = (section, config) => {
@@ -34,9 +35,10 @@ const getSectionComponents = (section, config) => {
  * Return an object for a given section with all components and subsections.
  * @param {object} section
  * @param {object} config
+ * @param {number} parentDepth
  * @returns {object}
  */
-function processSection(section, config) {
+function processSection(section, config, parentDepth) {
 	const contentRelativePath = section.content;
 
 	// Try to load section content file
@@ -49,11 +51,22 @@ function processSection(section, config) {
 		content = requireIt(`!!${examplesLoader}!${contentAbsolutePath}`);
 	}
 
+	let sectionDepth;
+
+	if (parentDepth === undefined) {
+		sectionDepth = section.sectionDepth !== undefined ? section.sectionDepth : 0;
+	} else {
+		sectionDepth = parentDepth === 0 ? 0 : parentDepth - 1;
+	}
+
 	return {
 		name: section.name,
+		exampleMode: section.exampleMode || config.exampleMode,
+		usageMode: section.usageMode || config.usageMode,
+		sectionDepth,
 		description: section.description,
 		slug: slugger.slug(section.name),
-		sections: getSections(section.sections || [], config),
+		sections: getSections(section.sections || [], config, sectionDepth),
 		filepath: contentRelativePath,
 		components: getSectionComponents(section, config),
 		content,
diff --git a/scripts/schemas/config.js b/scripts/schemas/config.js
index 1d94f6f9..a16ad08a 100644
--- a/scripts/schemas/config.js
+++ b/scripts/schemas/config.js
@@ -60,6 +60,13 @@ module.exports = {
 		process: val =>
 			val === true ? path.resolve(__dirname, '../templates/DefaultExample.md') : val,
 	},
+	exampleMode: {
+		type: 'string',
+		process: (value, config) => {
+			return config.showCode === undefined ? value : config.showCode ? 'expand' : 'collapse';
+		},
+		default: 'collapse',
+	},
 	getComponentPathLine: {
 		type: 'function',
 		default: componentPath => componentPath,
@@ -204,10 +211,12 @@ module.exports = {
 	showCode: {
 		type: 'boolean',
 		default: false,
+		deprecated: 'Use exampleMode option instead',
 	},
 	showUsage: {
 		type: 'boolean',
 		default: false,
+		deprecated: 'Use usageMode option instead',
 	},
 	showSidebar: {
 		type: 'boolean',
@@ -292,6 +301,13 @@ module.exports = {
 		type: 'function',
 		removed: `Use "webpackConfig" option instead:\n${consts.DOCS_WEBPACK}`,
 	},
+	usageMode: {
+		type: 'string',
+		process: (value, config) => {
+			return config.showUsage === undefined ? value : config.showUsage ? 'expand' : 'collapse';
+		},
+		default: 'collapse',
+	},
 	verbose: {
 		type: 'boolean',
 		default: false,
diff --git a/src/consts.js b/src/consts.js
index 36af2396..89efacb5 100644
--- a/src/consts.js
+++ b/src/consts.js
@@ -9,5 +9,18 @@ export const DisplayModes = Object.freeze({
 	component: 'component',
 	// Show one example inside component or section
 	example: 'example',
-	// TODO: error (404)
+	// Show error 404
+	notFound: 'notFound',
+});
+
+export const ExampleModes = Object.freeze({
+	hide: 'hide',
+	collapse: 'collapse',
+	expand: 'expand',
+});
+
+export const UsageModes = Object.freeze({
+	hide: 'hide',
+	collapse: 'collapse',
+	expand: 'expand',
 });
diff --git a/src/index.js b/src/index.js
index 564b249b..2e6319dd 100644
--- a/src/index.js
+++ b/src/index.js
@@ -3,15 +3,31 @@ import './polyfills';
 import './styles';
 import ReactDOM from 'react-dom';
 import renderStyleguide from './utils/renderStyleguide';
+import { getParameterByName, hasInHash } from './utils/handleHash';
 
 // 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. */
+// 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 hash = window.location.hash;
+	if (hasInHash(hash, '#/') || hasInHash(hash, '#!/')) {
+		// Extracts the id param of hash
+		const idHashParam = getParameterByName(hash, 'id');
+
+		// For default scroll scrollTop is the page top
+		let scrollTop = 0;
+
+		if (idHashParam) {
+			// Searches the node with the same id, takes his offsetTop
+			// And with offsetTop, tries to scroll to node
+			const idElement = document.getElementById(idHashParam);
+			if (idElement && idElement.offsetTop) {
+				scrollTop = idElement.offsetTop;
+			}
+		}
+		window.scrollTo(0, scrollTop);
 	}
 };
 
diff --git a/src/rsg-components/Components/Components.js b/src/rsg-components/Components/Components.js
index 22db69db..67cf87a8 100644
--- a/src/rsg-components/Components/Components.js
+++ b/src/rsg-components/Components/Components.js
@@ -3,11 +3,17 @@ import PropTypes from 'prop-types';
 import ReactComponent from 'rsg-components/ReactComponent';
 import ComponentsRenderer from 'rsg-components/Components/ComponentsRenderer';
 
-export default function Components({ components, depth }) {
+export default function Components({ components, depth, exampleMode, usageMode }) {
 	return (
 		<ComponentsRenderer>
 			{components.map(component => (
-				<ReactComponent key={component.filepath} component={component} depth={depth} />
+				<ReactComponent
+					key={component.filepath}
+					component={component}
+					exampleMode={exampleMode}
+					usageMode={usageMode}
+					depth={depth}
+				/>
 			))}
 		</ComponentsRenderer>
 	);
@@ -16,4 +22,6 @@ export default function Components({ components, depth }) {
 Components.propTypes = {
 	components: PropTypes.array.isRequired,
 	depth: PropTypes.number.isRequired,
+	exampleMode: PropTypes.string.isRequired,
+	usageMode: PropTypes.string.isRequired,
 };
diff --git a/src/rsg-components/Components/Components.spec.js b/src/rsg-components/Components/Components.spec.js
index 3858a364..68583f52 100644
--- a/src/rsg-components/Components/Components.spec.js
+++ b/src/rsg-components/Components/Components.spec.js
@@ -2,7 +2,10 @@ import React from 'react';
 import ReactComponent from '../ReactComponent';
 import Components from './Components';
 import ComponentsRenderer from './ComponentsRenderer';
+import { ExampleModes, UsageModes } from '../../consts';
 
+const exampleMode = ExampleModes.collapse;
+const usageMode = UsageModes.collapse;
 const components = [
 	{
 		name: 'Foo',
@@ -23,7 +26,9 @@ const components = [
 ];
 
 it('should render components list', () => {
-	const actual = shallow(<Components components={components} depth={3} />);
+	const actual = shallow(
+		<Components components={components} exampleMode={exampleMode} usageMode={usageMode} depth={3} />
+	);
 
 	expect(actual).toMatchSnapshot();
 });
@@ -31,8 +36,20 @@ it('should render components list', () => {
 it('renderer should render components list layout', () => {
 	const actual = shallow(
 		<ComponentsRenderer>
-			<ReactComponent key={0} component={components[0]} depth={3} />
-			<ReactComponent key={1} component={components[1]} depth={3} />
+			<ReactComponent
+				key={0}
+				component={components[0]}
+				exampleMode={exampleMode}
+				usageMode={usageMode}
+				depth={3}
+			/>
+			<ReactComponent
+				key={1}
+				component={components[1]}
+				exampleMode={exampleMode}
+				usageMode={usageMode}
+				depth={3}
+			/>
 		</ComponentsRenderer>
 	);
 
diff --git a/src/rsg-components/Components/__snapshots__/Components.spec.js.snap b/src/rsg-components/Components/__snapshots__/Components.spec.js.snap
index 960242a7..a8af4686 100644
--- a/src/rsg-components/Components/__snapshots__/Components.spec.js.snap
+++ b/src/rsg-components/Components/__snapshots__/Components.spec.js.snap
@@ -14,7 +14,9 @@ exports[`renderer should render components list layout 1`] = `
       }
     }
     depth={3}
+    exampleMode="collapse"
     key="0"
+    usageMode="collapse"
   />
   <ReactComponent
     component={
@@ -28,7 +30,9 @@ exports[`renderer should render components list layout 1`] = `
       }
     }
     depth={3}
+    exampleMode="collapse"
     key="1"
+    usageMode="collapse"
   />
 </div>
 `;
@@ -47,7 +51,9 @@ exports[`should render components list 1`] = `
       }
     }
     depth={3}
+    exampleMode="collapse"
     key="components/foo.js"
+    usageMode="collapse"
   />
   <ReactComponent
     component={
@@ -61,7 +67,9 @@ exports[`should render components list 1`] = `
       }
     }
     depth={3}
+    exampleMode="collapse"
     key="components/bar.js"
+    usageMode="collapse"
   />
 </ComponentsRenderer>
 `;
diff --git a/src/rsg-components/ComponentsList/ComponentsList.js b/src/rsg-components/ComponentsList/ComponentsList.js
index dcad24b9..48984d80 100644
--- a/src/rsg-components/ComponentsList/ComponentsList.js
+++ b/src/rsg-components/ComponentsList/ComponentsList.js
@@ -3,14 +3,15 @@ import ComponentsListRenderer from 'rsg-components/ComponentsList/ComponentsList
 import PropTypes from 'prop-types';
 import getUrl from '../../utils/getUrl';
 
-function ComponentsList({ classes, items, useIsolatedLinks = false }) {
+function ComponentsList({ classes, items, useRouterLinks = false, useHashId, hashPath }) {
 	const mappedItems = items.map(item => ({
 		...item,
 		href: getUrl({
 			name: item.name,
 			slug: item.slug,
-			anchor: !useIsolatedLinks,
-			isolated: useIsolatedLinks,
+			anchor: !useRouterLinks,
+			hashPath: useRouterLinks ? hashPath : false,
+			id: useRouterLinks ? useHashId : false,
 		}),
 	}));
 	return <ComponentsListRenderer classes={classes} items={mappedItems} />;
@@ -19,7 +20,9 @@ function ComponentsList({ classes, items, useIsolatedLinks = false }) {
 ComponentsList.propTypes = {
 	items: PropTypes.array.isRequired,
 	classes: PropTypes.object,
-	useIsolatedLinks: PropTypes.bool,
+	hashPath: PropTypes.array,
+	useRouterLinks: PropTypes.bool,
+	useHashId: PropTypes.bool,
 };
 
 export default ComponentsList;
diff --git a/src/rsg-components/ComponentsList/ComponentsList.spec.js b/src/rsg-components/ComponentsList/ComponentsList.spec.js
index c416d0b7..9f626c44 100644
--- a/src/rsg-components/ComponentsList/ComponentsList.spec.js
+++ b/src/rsg-components/ComponentsList/ComponentsList.spec.js
@@ -18,7 +18,7 @@ it('should set the correct href for items', () => {
 	expect(actual).toMatchSnapshot();
 });
 
-it('should set the correct href for items when isolated links should be used', () => {
+it('should set a parameter on link when useHashId is activated', () => {
 	const components = [
 		{
 			name: 'Button',
@@ -30,7 +30,39 @@ it('should set the correct href for items when isolated links should be used', (
 		},
 	];
 
-	const actual = shallow(<ComponentsList items={components} classes={{}} useIsolatedLinks />);
+	const actual = shallow(
+		<ComponentsList
+			items={components}
+			classes={{}}
+			useRouterLinks
+			hashPath={['Components']}
+			useHashId
+		/>
+	);
+	expect(actual).toMatchSnapshot();
+});
+
+it('should set a sub route on link when useHashId is deactivated', () => {
+	const components = [
+		{
+			name: 'Button',
+			slug: 'button',
+		},
+		{
+			name: 'Input',
+			slug: 'input',
+		},
+	];
+
+	const actual = shallow(
+		<ComponentsList
+			items={components}
+			classes={{}}
+			useRouterLinks
+			hashPath={['Components']}
+			useHashId={false}
+		/>
+	);
 	expect(actual).toMatchSnapshot();
 });
 
diff --git a/src/rsg-components/ComponentsList/ComponentsListRenderer.js b/src/rsg-components/ComponentsList/ComponentsListRenderer.js
index e4e6032e..e98bb591 100644
--- a/src/rsg-components/ComponentsList/ComponentsListRenderer.js
+++ b/src/rsg-components/ComponentsList/ComponentsListRenderer.js
@@ -60,7 +60,6 @@ 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 291827f8..6f9b8ac9 100644
--- a/src/rsg-components/ComponentsList/__snapshots__/ComponentsList.spec.js.snap
+++ b/src/rsg-components/ComponentsList/__snapshots__/ComponentsList.spec.js.snap
@@ -43,18 +43,18 @@ exports[`should render sections with nested components 1`] = `
 </ul>
 `;
 
-exports[`should set the correct href for items 1`] = `
+exports[`should set a parameter on link when useHashId is activated 1`] = `
 <Styled(ComponentsList)
   classes={Object {}}
   items={
     Array [
       Object {
-        "href": "blank#button",
+        "href": "blank#/Components?id=button",
         "name": "Button",
         "slug": "button",
       },
       Object {
-        "href": "blank#input",
+        "href": "blank#/Components?id=input",
         "name": "Input",
         "slug": "input",
       },
@@ -63,18 +63,38 @@ exports[`should set the correct href for items 1`] = `
 />
 `;
 
-exports[`should set the correct href for items when isolated links should be used 1`] = `
+exports[`should set a sub route on link when useHashId is deactivated 1`] = `
 <Styled(ComponentsList)
   classes={Object {}}
   items={
     Array [
       Object {
-        "href": "blank#!/Button",
+        "href": "blank#/Components/Button",
         "name": "Button",
         "slug": "button",
       },
       Object {
-        "href": "blank#!/Input",
+        "href": "blank#/Components/Input",
+        "name": "Input",
+        "slug": "input",
+      },
+    ]
+  }
+/>
+`;
+
+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",
       },
diff --git a/src/rsg-components/Examples/Examples.js b/src/rsg-components/Examples/Examples.js
index fce42309..c15663de 100644
--- a/src/rsg-components/Examples/Examples.js
+++ b/src/rsg-components/Examples/Examples.js
@@ -4,7 +4,7 @@ import Playground from 'rsg-components/Playground';
 import Markdown from 'rsg-components/Markdown';
 import ExamplesRenderer from 'rsg-components/Examples/ExamplesRenderer';
 
-export default function Examples({ examples, name }, { codeRevision }) {
+export default function Examples({ examples, name, exampleMode }, { codeRevision }) {
 	return (
 		<ExamplesRenderer>
 			{examples.map((example, index) => {
@@ -18,6 +18,7 @@ export default function Examples({ examples, name }, { codeRevision }) {
 								name={name}
 								index={index}
 								settings={example.settings}
+								exampleMode={exampleMode}
 							/>
 						);
 					case 'markdown':
@@ -32,6 +33,7 @@ export default function Examples({ examples, name }, { codeRevision }) {
 Examples.propTypes = {
 	examples: PropTypes.array.isRequired,
 	name: PropTypes.string.isRequired,
+	exampleMode: PropTypes.string.isRequired,
 };
 Examples.contextTypes = {
 	codeRevision: PropTypes.number.isRequired,
diff --git a/src/rsg-components/Examples/Examples.spec.js b/src/rsg-components/Examples/Examples.spec.js
index 383cdcd6..25b88844 100644
--- a/src/rsg-components/Examples/Examples.spec.js
+++ b/src/rsg-components/Examples/Examples.spec.js
@@ -16,7 +16,7 @@ const examples = [
 ];
 
 it('should render examples', () => {
-	const actual = shallow(<Examples examples={examples} name="button" />, {
+	const actual = shallow(<Examples examples={examples} name="button" exampleMode="collapse" />, {
 		context: {
 			codeRevision: 1,
 			displayMode: DisplayModes.example,
@@ -34,7 +34,7 @@ it('should not render a example with unknown type', () => {
 		},
 	];
 
-	const actual = mount(<Examples examples={faultyExample} name="button" />, {
+	const actual = mount(<Examples examples={faultyExample} name="button" exampleMode="collapse" />, {
 		context: {
 			codeRevision: 1,
 		},
diff --git a/src/rsg-components/Examples/__snapshots__/Examples.spec.js.snap b/src/rsg-components/Examples/__snapshots__/Examples.spec.js.snap
index f70e9e19..0beff09c 100644
--- a/src/rsg-components/Examples/__snapshots__/Examples.spec.js.snap
+++ b/src/rsg-components/Examples/__snapshots__/Examples.spec.js.snap
@@ -9,6 +9,7 @@ exports[`should render examples 1`] = `
 </button>
 
     evalInContext={[Function]}
+    exampleMode="collapse"
     index={0}
     key="1/0"
     name="button"
diff --git a/src/rsg-components/NotFound/NotFound.spec.js b/src/rsg-components/NotFound/NotFound.spec.js
new file mode 100644
index 00000000..0668d6f8
--- /dev/null
+++ b/src/rsg-components/NotFound/NotFound.spec.js
@@ -0,0 +1,8 @@
+import React from 'react';
+import { NotFoundRenderer } from './NotFoundRenderer';
+
+it('renderer should render not found message', () => {
+	const actual = shallow(<NotFoundRenderer classes={{}} />);
+
+	expect(actual).toMatchSnapshot();
+});
diff --git a/src/rsg-components/NotFound/NotFoundRenderer.js b/src/rsg-components/NotFound/NotFoundRenderer.js
new file mode 100644
index 00000000..6d98c5af
--- /dev/null
+++ b/src/rsg-components/NotFound/NotFoundRenderer.js
@@ -0,0 +1,32 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Markdown from 'rsg-components/Markdown';
+import Styled from 'rsg-components/Styled';
+
+const styles = ({ maxWidth }) => ({
+	root: {
+		maxWidth,
+		margin: [[0, 'auto']],
+	},
+});
+
+export function NotFoundRenderer({ classes }) {
+	return (
+		<div className={classes.root}>
+			<Markdown
+				text={`
+# Page not found
+
+The link you followed may be broken, or the page may have been removed.
+
+`}
+			/>
+		</div>
+	);
+}
+
+NotFoundRenderer.propTypes = {
+	classes: PropTypes.object.isRequired,
+};
+
+export default Styled(styles)(NotFoundRenderer);
diff --git a/src/rsg-components/NotFound/__snapshots__/NotFound.spec.js.snap b/src/rsg-components/NotFound/__snapshots__/NotFound.spec.js.snap
new file mode 100644
index 00000000..f97fefa8
--- /dev/null
+++ b/src/rsg-components/NotFound/__snapshots__/NotFound.spec.js.snap
@@ -0,0 +1,14 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renderer should render not found message 1`] = `
+<div>
+  <Markdown
+    text="
+# Page not found
+
+The link you followed may be broken, or the page may have been removed.
+
+"
+  />
+</div>
+`;
diff --git a/src/rsg-components/NotFound/index.js b/src/rsg-components/NotFound/index.js
new file mode 100644
index 00000000..712cac25
--- /dev/null
+++ b/src/rsg-components/NotFound/index.js
@@ -0,0 +1,1 @@
+export { default } from 'rsg-components/NotFound/NotFoundRenderer';
diff --git a/src/rsg-components/Playground/Playground.js b/src/rsg-components/Playground/Playground.js
index f31722a3..30bff31c 100644
--- a/src/rsg-components/Playground/Playground.js
+++ b/src/rsg-components/Playground/Playground.js
@@ -6,7 +6,7 @@ import Para from 'rsg-components/Para';
 import Slot from 'rsg-components/Slot';
 import PlaygroundRenderer from 'rsg-components/Playground/PlaygroundRenderer';
 import { EXAMPLE_TAB_CODE_EDITOR } from '../slots';
-import { DisplayModes } from '../../consts';
+import { DisplayModes, ExampleModes } from '../../consts';
 
 export default class Playground extends Component {
 	static propTypes = {
@@ -14,6 +14,7 @@ export default class Playground extends Component {
 		evalInContext: PropTypes.func.isRequired,
 		index: PropTypes.number.isRequired,
 		name: PropTypes.string.isRequired,
+		exampleMode: PropTypes.string.isRequired,
 		settings: PropTypes.object,
 	};
 
@@ -24,13 +25,14 @@ export default class Playground extends Component {
 
 	constructor(props, context) {
 		super(props, context);
-		const { code, settings } = props;
+		const { code, settings, exampleMode } = props;
 		const { config } = context;
-		const showCode = settings.showcode !== undefined ? settings.showcode : config.showCode;
+		const expandCode = exampleMode === ExampleModes.expand;
+		const activeTab = settings.showcode !== undefined ? settings.showcode : expandCode;
 
 		this.state = {
 			code,
-			activeTab: showCode ? EXAMPLE_TAB_CODE_EDITOR : undefined,
+			activeTab: activeTab ? EXAMPLE_TAB_CODE_EDITOR : undefined,
 		};
 
 		this.handleTabChange = this.handleTabChange.bind(this);
@@ -63,13 +65,15 @@ export default class Playground extends Component {
 
 	render() {
 		const { code, activeTab } = this.state;
-		const { evalInContext, index, name, settings } = this.props;
+		const { evalInContext, index, name, settings, exampleMode } = this.props;
 		const { displayMode } = this.context;
+		const isExampleHidden = exampleMode === ExampleModes.hide;
+		const isEditorHidden = settings.noeditor || isExampleHidden;
 		const preview = <Preview code={code} evalInContext={evalInContext} />;
-		if (settings.noeditor) {
-			return <Para>{preview}</Para>;
-		}
-		return (
+
+		return isEditorHidden ? (
+			<Para>{preview}</Para>
+		) : (
 			<PlaygroundRenderer
 				name={name}
 				preview={preview}
@@ -86,7 +90,7 @@ export default class Playground extends Component {
 						name="exampleTabs"
 						active={activeTab}
 						onlyActive
-						props={{ code, onChange: this.handleChange, evalInContext }}
+						props={{ code, onChange: this.handleChange }}
 					/>
 				}
 				toolbar={
diff --git a/src/rsg-components/Playground/Playground.spec.js b/src/rsg-components/Playground/Playground.spec.js
index 0df35c82..b472a7e7 100644
--- a/src/rsg-components/Playground/Playground.spec.js
+++ b/src/rsg-components/Playground/Playground.spec.js
@@ -13,13 +13,13 @@ const props = {
 	index: 0,
 	name: 'name',
 	settings: {},
+	exampleMode: 'collapse',
 	evalInContext,
 	code,
 };
 const options = {
 	context: {
 		config: {
-			showCode: false,
 			highlightTheme: 'base16-light',
 		},
 		codeRevision: 0,
@@ -95,13 +95,12 @@ it('should open a code editor by default if showcode=true option passed in examp
 	expect(actual.text()).toMatch('Loading');
 });
 
-it('should open a code editor by default if showCode=true option specified in style guide config', () => {
-	const actual = mount(<Playground {...props} />, {
+it('should open a code editor by default if exampleMode="expand" option specified in style guide config', () => {
+	const actual = mount(<Playground {...props} exampleMode="expand" />, {
 		context: {
 			...options.context,
 			config: {
 				...options.context.config,
-				showCode: true,
 			},
 		},
 		childContextTypes: options.childContextTypes,
@@ -110,16 +109,18 @@ it('should open a code editor by default if showCode=true option specified in st
 });
 
 it('showcode option in example settings should overwrite style guide config option', () => {
-	const actual = mount(<Playground {...props} settings={{ showcode: false }} />, {
-		context: {
-			...options.context,
-			config: {
-				...options.context.config,
-				showCode: true,
+	const actual = mount(
+		<Playground {...props} exampleMode="expand" settings={{ showcode: false }} />,
+		{
+			context: {
+				...options.context,
+				config: {
+					...options.context.config,
+				},
 			},
-		},
-		childContextTypes: options.childContextTypes,
-	});
+			childContextTypes: options.childContextTypes,
+		}
+	);
 	expect(actual.text()).not.toMatch('Loading');
 });
 
diff --git a/src/rsg-components/Playground/__snapshots__/Playground.spec.js.snap b/src/rsg-components/Playground/__snapshots__/Playground.spec.js.snap
index a5cc2a5a..e35f0f90 100644
--- a/src/rsg-components/Playground/__snapshots__/Playground.spec.js.snap
+++ b/src/rsg-components/Playground/__snapshots__/Playground.spec.js.snap
@@ -65,7 +65,6 @@ exports[`should render component renderer 1`] = `
   OK
 </button>
 ,
-          "evalInContext": [Function],
           "onChange": [Function],
         }
       }
diff --git a/src/rsg-components/ReactComponent/ReactComponent.js b/src/rsg-components/ReactComponent/ReactComponent.js
index bb9e1bf8..6e5c5ae6 100644
--- a/src/rsg-components/ReactComponent/ReactComponent.js
+++ b/src/rsg-components/ReactComponent/ReactComponent.js
@@ -7,7 +7,7 @@ import Markdown from 'rsg-components/Markdown';
 import Slot from 'rsg-components/Slot';
 import ReactComponentRenderer from 'rsg-components/ReactComponent/ReactComponentRenderer';
 import { DOCS_TAB_USAGE } from '../slots';
-import { DisplayModes } from '../../consts';
+import { DisplayModes, UsageModes } from '../../consts';
 
 const ExamplePlaceholder =
 	process.env.STYLEGUIDIST_ENV !== 'production'
@@ -18,6 +18,8 @@ export default class ReactComponent extends Component {
 	static propTypes = {
 		component: PropTypes.object.isRequired,
 		depth: PropTypes.number.isRequired,
+		exampleMode: PropTypes.string.isRequired,
+		usageMode: PropTypes.string.isRequired,
 	};
 	static contextTypes = {
 		config: PropTypes.object.isRequired,
@@ -26,12 +28,12 @@ export default class ReactComponent extends Component {
 
 	constructor(props, context) {
 		super(props, context);
-		const { showUsage } = context.config;
+		const { usageMode } = props;
 
 		this.handleTabChange = this.handleTabChange.bind(this);
 
 		this.state = {
-			activeTab: showUsage ? DOCS_TAB_USAGE : undefined,
+			activeTab: usageMode === UsageModes.expand ? DOCS_TAB_USAGE : undefined,
 		};
 	}
 
@@ -43,13 +45,14 @@ export default class ReactComponent extends Component {
 
 	render() {
 		const { activeTab } = this.state;
-		const { displayMode } = this.context;
-		const { component, depth } = this.props;
+		const { displayMode, config: { pagePerSection } } = this.context;
+		const { component, depth, usageMode, exampleMode } = this.props;
 		const { name, slug, filepath, pathLine } = component;
 		const { description, examples = [], tags = {} } = component.props;
 		if (!name) {
 			return null;
 		}
+		const showUsage = usageMode !== UsageModes.hide;
 
 		return (
 			<ReactComponentRenderer
@@ -62,6 +65,7 @@ export default class ReactComponent extends Component {
 				heading={
 					<SectionHeading
 						id={slug}
+						pagePerSection={pagePerSection}
 						deprecated={!!tags.deprecated}
 						slotName="componentToolbar"
 						slotProps={{
@@ -75,17 +79,19 @@ export default class ReactComponent extends Component {
 				}
 				examples={
 					examples.length > 0 ? (
-						<Examples examples={examples} name={name} />
+						<Examples examples={examples} name={name} exampleMode={exampleMode} />
 					) : (
 						<ExamplePlaceholder name={name} />
 					)
 				}
 				tabButtons={
-					<Slot
-						name="docsTabButtons"
-						active={activeTab}
-						props={{ ...component, onClick: this.handleTabChange }}
-					/>
+					showUsage && (
+						<Slot
+							name="docsTabButtons"
+							active={activeTab}
+							props={{ ...component, onClick: this.handleTabChange }}
+						/>
+					)
 				}
 				tabBody={<Slot name="docsTabs" active={activeTab} onlyActive props={component} />}
 			/>
diff --git a/src/rsg-components/ReactComponent/ReactComponent.spec.js b/src/rsg-components/ReactComponent/ReactComponent.spec.js
index e5ebf50e..806ef471 100644
--- a/src/rsg-components/ReactComponent/ReactComponent.spec.js
+++ b/src/rsg-components/ReactComponent/ReactComponent.spec.js
@@ -4,10 +4,14 @@ import ReactComponent from './ReactComponent';
 import { ReactComponentRenderer } from './ReactComponentRenderer';
 import { DisplayModes } from '../../consts';
 
+const exampleMode = 'collapse';
+const usageMode = 'collapse';
+
 const options = {
 	context: {
 		config: {
 			showUsage: false,
+			pagePerSection: false,
 		},
 		displayMode: DisplayModes.all,
 		slots,
@@ -65,7 +69,15 @@ const componentWithEverything = {
 
 describe('ReactComponent', () => {
 	it('should render an example placeholder', () => {
-		const actual = shallow(<ReactComponent component={component} depth={3} />, options);
+		const actual = shallow(
+			<ReactComponent
+				component={component}
+				depth={3}
+				exampleMode={exampleMode}
+				usageMode={usageMode}
+			/>,
+			options
+		);
 
 		const props = actual.prop('examples').props;
 		expect(props.name).toBeTruthy();
@@ -74,7 +86,12 @@ describe('ReactComponent', () => {
 
 	it('should render examples', () => {
 		const actual = shallow(
-			<ReactComponent component={componentWithEverything} depth={3} />,
+			<ReactComponent
+				component={componentWithEverything}
+				depth={3}
+				exampleMode={exampleMode}
+				usageMode={usageMode}
+			/>,
 			options
 		);
 
@@ -85,7 +102,12 @@ describe('ReactComponent', () => {
 
 	it('should pass rendered description, usage, examples, etc. to the renderer', () => {
 		const actual = shallow(
-			<ReactComponent component={componentWithEverything} depth={3} />,
+			<ReactComponent
+				component={componentWithEverything}
+				depth={3}
+				exampleMode={exampleMode}
+				usageMode={usageMode}
+			/>,
 			options
 		);
 
@@ -94,7 +116,12 @@ describe('ReactComponent', () => {
 
 	it('should render usage closed by default when showUsage config options is false', () => {
 		const actual = shallow(
-			<ReactComponent component={componentWithEverything} depth={3} />,
+			<ReactComponent
+				component={componentWithEverything}
+				depth={3}
+				exampleMode={exampleMode}
+				usageMode={usageMode}
+			/>,
 			options
 		);
 
@@ -103,14 +130,20 @@ describe('ReactComponent', () => {
 	});
 
 	it('should render usage opened by default when showUsage config options is true', () => {
-		const actual = shallow(<ReactComponent component={componentWithEverything} depth={3} />, {
-			...options,
-			context: {
-				config: {
-					showUsage: true,
+		const actual = shallow(
+			<ReactComponent
+				component={componentWithEverything}
+				depth={3}
+				exampleMode={exampleMode}
+				usageMode="expand"
+			/>,
+			{
+				...options,
+				context: {
+					config: {},
 				},
-			},
-		});
+			}
+		);
 
 		expect(actual.prop('tabButtons').props.active).toBe(DOCS_TAB_USAGE);
 		expect(actual.prop('tabBody').props.active).toBe(DOCS_TAB_USAGE);
@@ -118,7 +151,12 @@ describe('ReactComponent', () => {
 
 	it('should return null when component has no name', () => {
 		const actual = shallow(
-			<ReactComponent component={{ slug: 'foo', props: {} }} depth={3} />,
+			<ReactComponent
+				component={{ slug: 'foo', props: {} }}
+				depth={3}
+				exampleMode={exampleMode}
+				usageMode={usageMode}
+			/>,
 			options
 		);
 
@@ -126,30 +164,62 @@ describe('ReactComponent', () => {
 	});
 
 	test('should not render component in isolation mode by default', () => {
-		const actual = shallow(<ReactComponent component={component} depth={3} />, options);
+		const actual = shallow(
+			<ReactComponent
+				component={component}
+				depth={3}
+				exampleMode={exampleMode}
+				usageMode={usageMode}
+			/>,
+			options
+		);
 
 		expect(actual.prop('heading').props.slotProps.isolated).toBeFalsy();
 	});
 
 	test('should render component in isolation mode', () => {
-		const actual = shallow(<ReactComponent component={component} depth={3} />, {
-			context: {
-				...options.context,
-				displayMode: DisplayModes.component,
-			},
-		});
+		const actual = shallow(
+			<ReactComponent
+				component={component}
+				depth={3}
+				exampleMode={exampleMode}
+				usageMode={usageMode}
+			/>,
+			{
+				context: {
+					...options.context,
+					displayMode: DisplayModes.component,
+				},
+			}
+		);
 
 		expect(actual.prop('heading').props.slotProps.isolated).toBeTruthy();
 	});
 
 	it('should pass depth to heading', () => {
-		const actual = shallow(<ReactComponent component={component} depth={3} />, options);
+		const actual = shallow(
+			<ReactComponent
+				component={component}
+				depth={3}
+				exampleMode={exampleMode}
+				usageMode={usageMode}
+			/>,
+			options
+		);
 
 		expect(actual.prop('heading').props.depth).toBe(3);
 	});
 
 	it('should not render heading as deprecated by default', () => {
-		const actual = shallow(<ReactComponent component={component} depth={3} />, options);
+		const actual = shallow(
+			<ReactComponent
+				component={component}
+				depth={3}
+				exampleMode={exampleMode}
+				usageMode={usageMode}
+			/>,
+			options
+		);
 
 		expect(actual.prop('heading').props.deprecated).toBeFalsy();
 	});
@@ -171,6 +241,8 @@ describe('ReactComponent', () => {
 					},
 				}}
 				depth={3}
+				exampleMode={exampleMode}
+				usageMode={usageMode}
 			/>,
 			options
 		);
diff --git a/src/rsg-components/ReactComponent/__snapshots__/ReactComponent.spec.js.snap b/src/rsg-components/ReactComponent/__snapshots__/ReactComponent.spec.js.snap
index e0b89aa9..33a572eb 100644
--- a/src/rsg-components/ReactComponent/__snapshots__/ReactComponent.spec.js.snap
+++ b/src/rsg-components/ReactComponent/__snapshots__/ReactComponent.spec.js.snap
@@ -10,6 +10,7 @@ exports[`ReactComponent should pass rendered description, usage, examples, etc.
   docs={<JsDoc />}
   examples={
     <Examples
+      exampleMode="collapse"
       examples={
         Array [
           Object {
@@ -35,6 +36,7 @@ exports[`ReactComponent should pass rendered description, usage, examples, etc.
       deprecated={false}
       depth={3}
       id="foo"
+      pagePerSection={false}
       slotName="componentToolbar"
       slotProps={
         Object {
diff --git a/src/rsg-components/Section/Section.js b/src/rsg-components/Section/Section.js
index cc517ee9..5cb2ef27 100644
--- a/src/rsg-components/Section/Section.js
+++ b/src/rsg-components/Section/Section.js
@@ -6,16 +6,36 @@ import Sections from 'rsg-components/Sections';
 import SectionRenderer from 'rsg-components/Section/SectionRenderer';
 import { DisplayModes } from '../../consts';
 
-export default function Section({ section, depth }, { displayMode }) {
-	const { name, slug, filepath, content, components, sections, description } = section;
+export default function Section({ section, depth }, { displayMode, config: { pagePerSection } }) {
+	const {
+		name,
+		slug,
+		filepath,
+		content,
+		components,
+		sections,
+		description,
+		exampleMode,
+		usageMode,
+	} = section;
 
-	const contentJsx = content && <Examples examples={content} name={name} />;
-	const componentsJsx = components && <Components components={components} depth={depth + 1} />;
+	const contentJsx = content && (
+		<Examples examples={content} name={name} exampleMode={exampleMode} />
+	);
+	const componentsJsx = components && (
+		<Components
+			usageMode={usageMode}
+			exampleMode={exampleMode}
+			components={components}
+			depth={depth + 1}
+		/>
+	);
 	const sectionsJsx = sections && <Sections sections={sections} depth={depth + 1} />;
 
 	return (
 		<SectionRenderer
 			description={description}
+			pagePerSection={pagePerSection}
 			name={name}
 			slug={slug}
 			filepath={filepath}
@@ -35,4 +55,5 @@ Section.propTypes = {
 
 Section.contextTypes = {
 	displayMode: PropTypes.string,
+	config: PropTypes.object.isRequired,
 };
diff --git a/src/rsg-components/Section/Section.spec.js b/src/rsg-components/Section/Section.spec.js
index 427de234..99461a29 100644
--- a/src/rsg-components/Section/Section.spec.js
+++ b/src/rsg-components/Section/Section.spec.js
@@ -10,12 +10,17 @@ import { DisplayModes } from '../../consts';
 const options = {
 	context: {
 		displayMode: DisplayModes.all,
+		config: {
+			pagePerSection: false,
+		},
 	},
 };
 
 const section = {
 	name: 'Foo',
 	slug: 'foo',
+	exampleMode: 'collapse',
+	usageMode: 'collapse',
 	description: 'This is a description',
 	content: [
 		{
@@ -44,6 +49,8 @@ it('should render components list', () => {
 			section={{
 				name: 'Components',
 				slug: 'components',
+				usageMode: 'collapse',
+				exampleMode: 'collapse',
 				components: [],
 			}}
 			depth={3}
@@ -60,6 +67,8 @@ it('should not render components list if not defined', () => {
 			section={{
 				name: 'No components',
 				slug: 'no-components',
+				usageMode: 'collapse',
+				exampleMode: 'collapse',
 			}}
 			depth={3}
 		/>,
@@ -75,6 +84,8 @@ it('should render sections if defined', () => {
 			section={{
 				name: 'Nested sections',
 				slug: 'nested-sections',
+				usageMode: 'collapse',
+				exampleMode: 'collapse',
 				sections: [],
 			}}
 			depth={3}
@@ -91,6 +102,8 @@ it('should not render sections if not defined', () => {
 			section={{
 				name: 'No sections',
 				slug: 'no-sections',
+				usageMode: 'collapse',
+				exampleMode: 'collapse',
 			}}
 			depth={3}
 		/>,
@@ -106,6 +119,8 @@ test('should not render section in isolation mode by default', () => {
 			section={{
 				name: 'A',
 				slug: 'a',
+				usageMode: 'collapse',
+				exampleMode: 'collapse',
 			}}
 			depth={3}
 		/>,
@@ -121,6 +136,8 @@ test('should render section in isolation mode', () => {
 			section={{
 				name: 'A',
 				slug: 'a',
+				usageMode: 'collapse',
+				exampleMode: 'collapse',
 			}}
 			depth={3}
 		/>,
@@ -141,8 +158,21 @@ it('render should render section', () => {
 			classes={{}}
 			name={section.name}
 			slug={section.slug}
-			content={<Examples name={section.name} examples={section.content} />}
-			components={<Components components={[]} depth={3} />}
+			content={
+				<Examples
+					name={section.name}
+					examples={section.content}
+					exampleMode={section.exampleMode}
+				/>
+			}
+			components={
+				<Components
+					components={[]}
+					depth={3}
+					usageMode={section.usageMode}
+					exampleMode={section.exampleMode}
+				/>
+			}
 			sections={<Sections sections={[]} depth={3} />}
 			depth={3}
 		/>,
diff --git a/src/rsg-components/Section/SectionRenderer.js b/src/rsg-components/Section/SectionRenderer.js
index 32c982f0..9520b965 100644
--- a/src/rsg-components/Section/SectionRenderer.js
+++ b/src/rsg-components/Section/SectionRenderer.js
@@ -11,12 +11,28 @@ const styles = ({ space }) => ({
 });
 
 export function SectionRenderer(allProps) {
-	const { classes, name, slug, content, components, sections, depth, description } = allProps;
+	const {
+		classes,
+		name,
+		slug,
+		content,
+		components,
+		sections,
+		depth,
+		description,
+		pagePerSection,
+	} = allProps;
 
 	return (
 		<section className={classes.root}>
 			{name && (
-				<SectionHeading depth={depth} id={slug} slotName="sectionToolbar" slotProps={allProps}>
+				<SectionHeading
+					depth={depth}
+					id={slug}
+					slotName="sectionToolbar"
+					pagePerSection={pagePerSection}
+					slotProps={allProps}
+				>
 					{name}
 				</SectionHeading>
 			)}
@@ -39,6 +55,7 @@ SectionRenderer.propTypes = {
 	sections: PropTypes.node,
 	isolated: PropTypes.bool,
 	depth: PropTypes.number.isRequired,
+	pagePerSection: PropTypes.bool,
 };
 
 export default Styled(styles)(SectionRenderer);
diff --git a/src/rsg-components/Section/__snapshots__/Section.spec.js.snap b/src/rsg-components/Section/__snapshots__/Section.spec.js.snap
index 3d55ee99..70d50ef8 100644
--- a/src/rsg-components/Section/__snapshots__/Section.spec.js.snap
+++ b/src/rsg-components/Section/__snapshots__/Section.spec.js.snap
@@ -14,8 +14,11 @@ exports[`render should render section 1`] = `
         "components": <Components
           components={Array []}
           depth={3}
+          exampleMode="collapse"
+          usageMode="collapse"
         />,
         "content": <Examples
+          exampleMode="collapse"
           examples={
             Array [
               Object {
@@ -48,6 +51,7 @@ exports[`render should render section 1`] = `
     Foo
   </SectionHeading>
   <Examples
+    exampleMode="collapse"
     examples={
       Array [
         Object {
@@ -74,6 +78,8 @@ exports[`render should render section 1`] = `
   <Components
     components={Array []}
     depth={3}
+    exampleMode="collapse"
+    usageMode="collapse"
   />
 </section>
 `;
@@ -103,6 +109,7 @@ exports[`should not render components list if not defined 1`] = `
   depth={3}
   isolated={false}
   name="No components"
+  pagePerSection={false}
   slug="no-components"
 />
 `;
@@ -112,6 +119,7 @@ exports[`should not render sections if not defined 1`] = `
   depth={3}
   isolated={false}
   name="No sections"
+  pagePerSection={false}
   slug="no-sections"
 />
 `;
@@ -122,11 +130,14 @@ exports[`should render components list 1`] = `
     <Components
       components={Array []}
       depth={4}
+      exampleMode="collapse"
+      usageMode="collapse"
     />
   }
   depth={3}
   isolated={false}
   name="Components"
+  pagePerSection={false}
   slug="components"
 />
 `;
@@ -137,10 +148,13 @@ exports[`should render section renderer 1`] = `
     <Components
       components={Array []}
       depth={4}
+      exampleMode="collapse"
+      usageMode="collapse"
     />
   }
   content={
     <Examples
+      exampleMode="collapse"
       examples={
         Array [
           Object {
@@ -165,6 +179,7 @@ exports[`should render section renderer 1`] = `
   description="This is a description"
   isolated={false}
   name="Foo"
+  pagePerSection={false}
   sections={
     <Sections
       depth={4}
@@ -180,6 +195,7 @@ exports[`should render sections if defined 1`] = `
   depth={3}
   isolated={false}
   name="Nested sections"
+  pagePerSection={false}
   sections={
     <Sections
       depth={4}
diff --git a/src/rsg-components/SectionHeading/SectionHeading.js b/src/rsg-components/SectionHeading/SectionHeading.js
index 05139006..ab0c8c18 100644
--- a/src/rsg-components/SectionHeading/SectionHeading.js
+++ b/src/rsg-components/SectionHeading/SectionHeading.js
@@ -4,8 +4,17 @@ import Slot from 'rsg-components/Slot';
 import SectionHeadingRenderer from 'rsg-components/SectionHeading/SectionHeadingRenderer';
 import getUrl from '../../utils/getUrl';
 
-export default function SectionHeading({ slotName, slotProps, children, id, ...rest }) {
-	const href = getUrl({ slug: id, anchor: true });
+export default function SectionHeading({
+	slotName,
+	slotProps,
+	children,
+	id,
+	pagePerSection,
+	...rest
+}) {
+	const href = pagePerSection
+		? getUrl({ slug: id, id: rest.depth !== 1, takeHash: true })
+		: getUrl({ slug: id, anchor: true });
 	return (
 		<SectionHeadingRenderer
 			toolbar={<Slot name={slotName} props={slotProps} />}
@@ -25,4 +34,5 @@ SectionHeading.propTypes = {
 	slotProps: PropTypes.object.isRequired,
 	depth: PropTypes.number.isRequired,
 	deprecated: PropTypes.bool,
+	pagePerSection: PropTypes.bool,
 };
diff --git a/src/rsg-components/SectionHeading/SectionHeading.spec.js b/src/rsg-components/SectionHeading/SectionHeading.spec.js
index a1765075..348360ff 100644
--- a/src/rsg-components/SectionHeading/SectionHeading.spec.js
+++ b/src/rsg-components/SectionHeading/SectionHeading.spec.js
@@ -55,4 +55,25 @@ describe('SectionHeading', () => {
 				.prop('level')
 		).toEqual(6);
 	});
+
+	it('should the href have a parameter id=section', () => {
+		const actual = shallow(
+			<SectionHeading
+				id="section"
+				pagePerSection
+				slotName="slot"
+				slotProps={{ foo: 1, bar: 'baz' }}
+				depth={2}
+			>
+				A Section
+			</SectionHeading>
+		);
+
+		expect(
+			actual
+				.dive()
+				.find('SectionHeadingRenderer')
+				.prop('href')
+		).toEqual('blank?id=section');
+	});
 });
diff --git a/src/rsg-components/StyleGuide/StyleGuide.js b/src/rsg-components/StyleGuide/StyleGuide.js
index a2cd8954..e3164c42 100644
--- a/src/rsg-components/StyleGuide/StyleGuide.js
+++ b/src/rsg-components/StyleGuide/StyleGuide.js
@@ -5,6 +5,7 @@ import StyleGuideRenderer from 'rsg-components/StyleGuide/StyleGuideRenderer';
 import Sections from 'rsg-components/Sections';
 import Welcome from 'rsg-components/Welcome';
 import Error from 'rsg-components/Error';
+import NotFound from 'rsg-components/NotFound';
 import { HOMEPAGE } from '../../../scripts/consts';
 import { DisplayModes } from '../../consts';
 
@@ -24,11 +25,8 @@ import { DisplayModes } from '../../consts';
  * @param {boolean} pagePerSection
  * @returns {boolean}
  */
-function hasSidebar(displayMode, showSidebar, pagePerSection = false) {
-	return (
-		(pagePerSection && displayMode !== DisplayModes.example) ||
-		(showSidebar && displayMode === DisplayModes.all)
-	);
+function hasSidebar(displayMode, showSidebar) {
+	return displayMode === DisplayModes.notFound || (showSidebar && displayMode === DisplayModes.all);
 }
 
 export default class StyleGuide extends Component {
@@ -99,10 +97,10 @@ export default class StyleGuide extends Component {
 			<StyleGuideRenderer
 				title={config.title}
 				homepageUrl={HOMEPAGE}
-				toc={<TableOfContents sections={allSections} useIsolatedLinks={pagePerSection} />}
-				hasSidebar={hasSidebar(displayMode, config.showSidebar, pagePerSection)}
+				toc={<TableOfContents sections={allSections} useRouterLinks={pagePerSection} />}
+				hasSidebar={hasSidebar(displayMode, config.showSidebar)}
 			>
-				<Sections sections={sections} depth={1} />
+				{sections.length ? <Sections sections={sections} depth={1} /> : <NotFound />}
 			</StyleGuideRenderer>
 		);
 	}
diff --git a/src/rsg-components/StyleGuide/StyleGuide.spec.js b/src/rsg-components/StyleGuide/StyleGuide.spec.js
index f3e0e225..59642e21 100644
--- a/src/rsg-components/StyleGuide/StyleGuide.spec.js
+++ b/src/rsg-components/StyleGuide/StyleGuide.spec.js
@@ -36,6 +36,7 @@ it('should render components list', () => {
 		<StyleGuide
 			codeRevision={1}
 			config={config}
+			pagePerSection={false}
 			sections={sections}
 			allSections={sections}
 			slots={{}}
@@ -126,7 +127,7 @@ describe('sidebar rendering', () => {
 				sections={sections}
 				allSections={sections}
 				slots={{}}
-				displayMode={DisplayModes.component}
+				displayMode={DisplayModes.all}
 				pagePerSection
 			/>
 		);
diff --git a/src/rsg-components/StyleGuide/__snapshots__/StyleGuide.spec.js.snap b/src/rsg-components/StyleGuide/__snapshots__/StyleGuide.spec.js.snap
index 121b975b..23939dee 100644
--- a/src/rsg-components/StyleGuide/__snapshots__/StyleGuide.spec.js.snap
+++ b/src/rsg-components/StyleGuide/__snapshots__/StyleGuide.spec.js.snap
@@ -99,7 +99,7 @@ exports[`should render components list 1`] = `
           },
         ]
       }
-      useIsolatedLinks={undefined}
+      useRouterLinks={false}
     />
   }
 >
diff --git a/src/rsg-components/TableOfContents/TableOfContents.js b/src/rsg-components/TableOfContents/TableOfContents.js
index 01b6dc42..02b71b41 100644
--- a/src/rsg-components/TableOfContents/TableOfContents.js
+++ b/src/rsg-components/TableOfContents/TableOfContents.js
@@ -7,32 +7,44 @@ import filterSectionsByName from '../../utils/filterSectionsByName';
 export default class TableOfContents extends Component {
 	static propTypes = {
 		sections: PropTypes.array.isRequired,
-		useIsolatedLinks: PropTypes.bool,
+		useRouterLinks: PropTypes.bool,
 	};
 	state = {
 		searchTerm: '',
 	};
 
-	renderLevel(sections, useIsolatedLinks = false) {
+	renderLevel(sections, useRouterLinks = false, hashPath = [], useHashId = false) {
 		const items = sections.map(section => {
 			const children = [...(section.sections || []), ...(section.components || [])];
+			const sectionDepth = section.sectionDepth || 0;
+			const childHashPath =
+				sectionDepth === 0 && useHashId ? hashPath : [...hashPath, section.name];
 			return Object.assign({}, section, {
 				heading: !!section.name && children.length > 0,
-				content: children.length > 0 && this.renderLevel(children, useIsolatedLinks),
+				content:
+					children.length > 0 &&
+					this.renderLevel(children, useRouterLinks, childHashPath, sectionDepth === 0),
 			});
 		});
-		return <ComponentsList items={items} useIsolatedLinks={useIsolatedLinks} />;
+		return (
+			<ComponentsList
+				items={items}
+				hashPath={hashPath}
+				useHashId={useHashId}
+				useRouterLinks={useRouterLinks}
+			/>
+		);
 	}
 
 	renderSections() {
 		const { searchTerm } = this.state;
-		const { sections, useIsolatedLinks } = this.props;
+		const { sections, useRouterLinks } = 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, useIsolatedLinks);
+		return this.renderLevel(filtered, useRouterLinks);
 	}
 
 	render() {
diff --git a/src/rsg-components/TableOfContents/__snapshots__/TableOfContents.spec.js.snap b/src/rsg-components/TableOfContents/__snapshots__/TableOfContents.spec.js.snap
index f31b586e..f5a084d3 100644
--- a/src/rsg-components/TableOfContents/__snapshots__/TableOfContents.spec.js.snap
+++ b/src/rsg-components/TableOfContents/__snapshots__/TableOfContents.spec.js.snap
@@ -22,6 +22,7 @@ exports[`should filter list when search field contains a query 1`] = `
   searchTerm=""
 >
   <ComponentsList
+    hashPath={Array []}
     items={
       Array [
         Object {
@@ -50,7 +51,8 @@ exports[`should filter list when search field contains a query 1`] = `
         },
       ]
     }
-    useIsolatedLinks={false}
+    useHashId={false}
+    useRouterLinks={false}
   />
 </Styled(TableOfContents)>
 `;
@@ -61,6 +63,7 @@ exports[`should filter list when search field contains a query 2`] = `
   searchTerm="but"
 >
   <ComponentsList
+    hashPath={Array []}
     items={
       Array [
         Object {
@@ -73,7 +76,8 @@ exports[`should filter list when search field contains a query 2`] = `
         },
       ]
     }
-    useIsolatedLinks={false}
+    useHashId={false}
+    useRouterLinks={false}
   />
 </Styled(TableOfContents)>
 `;
@@ -84,6 +88,7 @@ exports[`should filter section names 1`] = `
   searchTerm=""
 >
   <ComponentsList
+    hashPath={Array []}
     items={
       Array [
         Object {
@@ -102,6 +107,11 @@ exports[`should filter section names 1`] = `
             },
           ],
           "content": <ComponentsList
+            hashPath={
+              Array [
+                "Buttons",
+              ]
+            }
             items={
               Array [
                 Object {
@@ -112,7 +122,8 @@ exports[`should filter section names 1`] = `
                 },
               ]
             }
-            useIsolatedLinks={false}
+            useHashId={true}
+            useRouterLinks={false}
           />,
           "heading": true,
           "name": "Buttons",
@@ -131,6 +142,11 @@ exports[`should filter section names 1`] = `
             },
           ],
           "content": <ComponentsList
+            hashPath={
+              Array [
+                "Forms",
+              ]
+            }
             items={
               Array [
                 Object {
@@ -147,7 +163,8 @@ exports[`should filter section names 1`] = `
                 },
               ]
             }
-            useIsolatedLinks={false}
+            useHashId={true}
+            useRouterLinks={false}
           />,
           "heading": true,
           "name": "Forms",
@@ -156,7 +173,8 @@ exports[`should filter section names 1`] = `
         },
       ]
     }
-    useIsolatedLinks={false}
+    useHashId={false}
+    useRouterLinks={false}
   />
 </Styled(TableOfContents)>
 `;
@@ -167,6 +185,7 @@ exports[`should filter section names 2`] = `
   searchTerm="frm"
 >
   <ComponentsList
+    hashPath={Array []}
     items={
       Array [
         Object {
@@ -179,7 +198,8 @@ exports[`should filter section names 2`] = `
         },
       ]
     }
-    useIsolatedLinks={false}
+    useHashId={false}
+    useRouterLinks={false}
   />
 </Styled(TableOfContents)>
 `;
@@ -190,6 +210,7 @@ exports[`should render a renderer 1`] = `
   searchTerm=""
 >
   <ComponentsList
+    hashPath={Array []}
     items={
       Array [
         Object {
@@ -218,7 +239,8 @@ exports[`should render a renderer 1`] = `
         },
       ]
     }
-    useIsolatedLinks={false}
+    useHashId={false}
+    useRouterLinks={false}
   />
 </Styled(TableOfContents)>
 `;
@@ -229,6 +251,7 @@ exports[`should render renderer with sections with nested components 1`] = `
   searchTerm=""
 >
   <ComponentsList
+    hashPath={Array []}
     items={
       Array [
         Object {
@@ -247,6 +270,11 @@ exports[`should render renderer with sections with nested components 1`] = `
             },
           ],
           "content": <ComponentsList
+            hashPath={
+              Array [
+                "Buttons",
+              ]
+            }
             items={
               Array [
                 Object {
@@ -257,7 +285,8 @@ exports[`should render renderer with sections with nested components 1`] = `
                 },
               ]
             }
-            useIsolatedLinks={false}
+            useHashId={true}
+            useRouterLinks={false}
           />,
           "heading": true,
           "name": "Buttons",
@@ -276,6 +305,11 @@ exports[`should render renderer with sections with nested components 1`] = `
             },
           ],
           "content": <ComponentsList
+            hashPath={
+              Array [
+                "Forms",
+              ]
+            }
             items={
               Array [
                 Object {
@@ -292,7 +326,8 @@ exports[`should render renderer with sections with nested components 1`] = `
                 },
               ]
             }
-            useIsolatedLinks={false}
+            useHashId={true}
+            useRouterLinks={false}
           />,
           "heading": true,
           "name": "Forms",
@@ -301,7 +336,8 @@ exports[`should render renderer with sections with nested components 1`] = `
         },
       ]
     }
-    useIsolatedLinks={false}
+    useHashId={false}
+    useRouterLinks={false}
   />
 </Styled(TableOfContents)>
 `;
diff --git a/src/rsg-components/slots/index.js b/src/rsg-components/slots/index.js
index 0301f39d..66f6046c 100644
--- a/src/rsg-components/slots/index.js
+++ b/src/rsg-components/slots/index.js
@@ -7,8 +7,8 @@ import UsageTabButton from './UsageTabButton';
 export const EXAMPLE_TAB_CODE_EDITOR = 'rsg-code-editor';
 export const DOCS_TAB_USAGE = 'rsg-usage';
 
-export default ({ pagePerSection }) => {
-	const toolbar = pagePerSection ? [] : [IsolateButton];
+export default () => {
+	const toolbar = [IsolateButton];
 
 	return {
 		sectionToolbar: toolbar,
diff --git a/src/rsg-components/slots/index.spec.js b/src/rsg-components/slots/index.spec.js
deleted file mode 100644
index eda6fe19..00000000
--- a/src/rsg-components/slots/index.spec.js
+++ /dev/null
@@ -1,18 +0,0 @@
-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/filterComponentsInSectionsByExactName.js b/src/utils/filterComponentsInSectionsByExactName.js
index b0005fed..e9378b5c 100644
--- a/src/utils/filterComponentsInSectionsByExactName.js
+++ b/src/utils/filterComponentsInSectionsByExactName.js
@@ -5,17 +5,27 @@ import filterComponentsByExactName from './filterComponentsByExactName';
  *
  * @param {object} sections
  * @param {string} name
+ * @param {boolean} recursive
  * @return {Array}
  */
-export default function filterComponentsInSectionsByExactName(sections, name) {
-	const components = [];
+export default function filterComponentsInSectionsByExactName(sections, name, recursive) {
+	const filteredSections = [];
 	sections.forEach(section => {
 		if (section.components) {
-			components.push(...filterComponentsByExactName(section.components, name));
+			const filteredComponents = filterComponentsByExactName(section.components, name);
+			if (filteredComponents.length) {
+				filteredSections.push({
+					exampleMode: section.exampleMode,
+					usageMode: section.usageMode,
+					components: filteredComponents,
+				});
+			}
 		}
-		if (section.sections) {
-			components.push(...filterComponentsInSectionsByExactName(section.sections, name));
+		if (section.sections && recursive) {
+			filteredSections.push(
+				...filterComponentsInSectionsByExactName(section.sections, name, recursive)
+			);
 		}
 	});
-	return components;
+	return filteredSections;
 }
diff --git a/src/utils/getInfoFromHash.js b/src/utils/getInfoFromHash.js
index bd3ac477..e88a7c10 100644
--- a/src/utils/getInfoFromHash.js
+++ b/src/utils/getInfoFromHash.js
@@ -1,4 +1,9 @@
 import isNaN from 'lodash/isNaN';
+import { hasInHash, getHashAsArray } from './handleHash';
+
+function filterNumbers(item) {
+	return isNaN(parseInt(item, 10)) && item !== '';
+}
 
 /**
  * Returns an object containing component/section name and, optionally, an example index
@@ -10,11 +15,14 @@ import isNaN from 'lodash/isNaN';
  * @returns {object}
  */
 export default function getInfoFromHash(hash) {
-	if (hash.substr(0, 3) === '#!/') {
-		const tokens = hash.substr(3).split('/');
-		const index = parseInt(tokens[1], 10);
+	const shouldIsolate = hasInHash(hash, '#!/');
+	if (shouldIsolate || hasInHash(hash, '#/')) {
+		const hashArray = getHashAsArray(hash, shouldIsolate ? '#!/' : '#/');
+		const index = parseInt(hashArray[hashArray.length - 1], 10);
 		return {
-			targetName: decodeURIComponent(tokens[0]),
+			isolate: shouldIsolate,
+			hashArray: hashArray.filter(filterNumbers),
+			targetName: hashArray[0],
 			targetIndex: isNaN(index) ? undefined : index,
 		};
 	}
diff --git a/src/utils/getPageTitle.js b/src/utils/getPageTitle.js
index ecb32343..38f02ef3 100644
--- a/src/utils/getPageTitle.js
+++ b/src/utils/getPageTitle.js
@@ -13,11 +13,19 @@ import { DisplayModes } from '../consts';
  * @return {string}
  */
 export default function getPageTitle(sections, baseTitle, displayMode) {
-	if (displayMode === DisplayModes.component || displayMode === DisplayModes.example) {
-		const name = get(sections[0], 'components.0.name', sections[0].name);
-		return `${name} — ${baseTitle}`;
-	} else if (displayMode === DisplayModes.section) {
-		return `${sections[0].name} — ${baseTitle}`;
+	if (displayMode === DisplayModes.notFound) {
+		return 'Page not found';
+	}
+	if (sections.length) {
+		if (
+			displayMode === DisplayModes.component ||
+			(displayMode === DisplayModes.example && sections[0].components)
+		) {
+			const name = get(sections[0], 'components.0.name', sections[0].name);
+			return `${name} — ${baseTitle}`;
+		} else if (displayMode === DisplayModes.section || displayMode === DisplayModes.example) {
+			return `${sections[0].name} — ${baseTitle}`;
+		}
 	}
 	return baseTitle;
 }
diff --git a/src/utils/getRouteData.js b/src/utils/getRouteData.js
index 23d8276f..06debc5f 100644
--- a/src/utils/getRouteData.js
+++ b/src/utils/getRouteData.js
@@ -6,21 +6,6 @@ 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.
  *
@@ -35,40 +20,100 @@ const getFirstSectionOrComponent = sections => {
  */
 export default function getRouteData(sections, hash, pagePerSection) {
 	// Parse URL hash to check if the components list must be filtered
+	const infoFromHash = getInfoFromHash(hash, pagePerSection);
+
+	// Name of the filtered component/section to show isolated (/#!/Button → Button)
+	let { targetName, hashArray } = infoFromHash;
+
 	const {
-		// Name of the filtered component/section to show isolated (/#!/Button → Button)
-		targetName,
 		// Index of the fenced block example of the filtered component isolate (/#!/Button/1 → 1)
 		targetIndex,
-	} = getInfoFromHash(hash);
+		isolate,
+	} = infoFromHash;
 
-	let displayMode = DisplayModes.all;
+	let displayMode = isolate ? DisplayModes.example : DisplayModes.all;
+
+	if (pagePerSection && !targetName && sections[0]) {
+		// For default takes the first section when pagePerSection enabled
+		targetName = sections[0].name;
+		hashArray = [targetName];
+	}
 
-	// Filter the requested component if required
 	if (targetName) {
-		const filteredComponents = filterComponentsInSectionsByExactName(sections, targetName);
-		if (filteredComponents.length) {
-			sections = [{ components: filteredComponents }];
-			displayMode = DisplayModes.component;
+		let filteredSections;
+
+		if (pagePerSection) {
+			// hashArray could be an array as ["Documentation", "Files", "Button"]
+			// each hashArray's element represent each section name with the same deep
+			// so it should be filter each section to trying to find each one of array on the same deep
+			hashArray.forEach((hashName, index) => {
+				// Filter the requested component if required but only on the first depth
+				// so in the next time of iteration, it will be trying to filter only on the second depth and so on
+				filteredSections = filterComponentsInSectionsByExactName(sections, hashName, false);
+
+				// If filteredSections exists, its because is an array of an component
+				// else it is an array of sections and depending his sectionDepth
+				// his children could be filtered or not
+				if (filteredSections.length) {
+					sections = filteredSections;
+				} else {
+					let section = findSection(sections, hashName);
+					if (section) {
+						// Only if hashName is the last of hashArray his children should be filtered
+						// because else there are possibilities to keep on filtering to try find the next section
+						const isLastHashName = !hashArray[index + 1];
+
+						// When sectionDepth is bigger than 0, their children should be filtered
+						const shouldFilterTheirChildren = section.sectionDepth > 0 && isLastHashName;
+
+						if (shouldFilterTheirChildren) {
+							// Filter his sections and components
+							section = {
+								...section,
+								sections: [],
+								components: [],
+							};
+						}
+						sections = [section];
+					} else {
+						sections = [];
+					}
+				}
+			});
+			if (!sections.length) {
+				displayMode = DisplayModes.notFound;
+			}
+			// The targetName takes the last of hashArray
+			targetName = hashArray[hashArray.length - 1];
 		} else {
-			const section = findSection(sections, targetName);
-			sections = section ? [section] : [];
-			displayMode = DisplayModes.section;
+			// Filter the requested component if required
+			filteredSections = filterComponentsInSectionsByExactName(sections, targetName, true);
+			if (filteredSections.length) {
+				sections = filteredSections;
+				displayMode = DisplayModes.component;
+			} else {
+				const section = findSection(sections, targetName);
+				sections = section ? [section] : [];
+				displayMode = DisplayModes.section;
+			}
 		}
 
 		// If a single component or section is filtered and a fenced block index is specified hide all other examples
 		if (isFinite(targetIndex)) {
-			if (filteredComponents.length === 1) {
-				sections = [{ components: [filterComponentExamples(filteredComponents[0], targetIndex)] }];
+			if (filteredSections.length === 1) {
+				const filteredComponents = filteredSections[0].components;
+				sections = [
+					{
+						...filteredSections[0],
+						components: [filterComponentExamples(filteredComponents[0], targetIndex)],
+					},
+				];
 				displayMode = DisplayModes.example;
 			} else if (sections.length === 1) {
 				sections = [filterSectionExamples(sections[0], targetIndex)];
 				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/getUrl.js b/src/utils/getUrl.js
index a35b0788..fbb6ce7a 100644
--- a/src/utils/getUrl.js
+++ b/src/utils/getUrl.js
@@ -12,11 +12,19 @@
  * @return {string}
  */
 export default function getUrl(
-	{ name, slug, example, anchor, isolated, nochrome, absolute } = {},
-	{ origin, pathname } = window.location
+	{ name, slug, example, anchor, isolated, nochrome, absolute, hashPath, id, takeHash } = {},
+	{ origin, pathname, hash } = window.location
 ) {
 	let url = pathname;
 
+	if (takeHash) {
+		if (hash.indexOf('?') > -1) {
+			url += hash.substring(0, hash.indexOf('?'));
+		} else {
+			url += hash;
+		}
+	}
+
 	if (nochrome) {
 		url += '?nochrome';
 	}
@@ -27,6 +35,17 @@ export default function getUrl(
 		url += `#!/${name}`;
 	}
 
+	if (hashPath) {
+		if (!id) {
+			hashPath = [...hashPath, name];
+		}
+		url += `#/${hashPath.join('/')}`;
+	}
+
+	if (id) {
+		url += `?id=${slug}`;
+	}
+
 	if (example !== undefined) {
 		url += `/${example}`;
 	}
diff --git a/src/utils/handleHash.js b/src/utils/handleHash.js
new file mode 100644
index 00000000..57b1c62b
--- /dev/null
+++ b/src/utils/handleHash.js
@@ -0,0 +1,75 @@
+import escapeRegExp from 'lodash/escapeRegExp';
+
+// We’re using this file to handle the hash to develop the routes, there are two types of hash '#/' and '#!/'
+// However, it is a temporal solution because is necessary using a library third-party that it is his focus
+// You can find more information here:
+// https://github.com/styleguidist/react-styleguidist/pull/993
+const defaultPrependHash = '#/';
+const separator = '/';
+const hashValRegexp = /(.*)\?/;
+
+function trimHash(hash, prependHash) {
+	if (!hash) {
+		return '';
+	}
+	const regexp = new RegExp('^' + escapeRegExp(prependHash || defaultPrependHash), 'g');
+	return hash.replace(regexp, '');
+}
+
+const trimParams = hash => {
+	const result = hashValRegexp.exec(hash);
+	return (result && result[1]) || hash;
+};
+
+/**
+ * If hash has a certain element
+ *
+ * @param {string} hash
+ * @param {string} search
+ * @return {boolean}
+ */
+export const hasInHash = (hash, search) => {
+	return hash !== '' && hash.indexOf(search) > -1;
+};
+
+/**
+ * Get hash value without '#', prependHash and parameters
+ *
+ * @param {string} hash
+ * @param {string} prependHash
+ * @return {string}
+ */
+export const getHash = (hash, prependHash) => {
+	return decodeURIComponent(trimParams(trimHash(hash, prependHash)));
+};
+
+/**
+ * Get hash value split into an Array.
+ *
+ * @param {string} hash
+ * @param {string} prependHash
+ * @return {Array.<string>}
+ */
+export const getHashAsArray = (hash, prependHash) => {
+	return getHash(hash, prependHash).split(separator);
+};
+
+/**
+ * Get a parameter by name in hash
+ *
+ * @param {string} hash
+ * @param {string} name
+ * @return {string}
+ */
+export const getParameterByName = (hash, name) => {
+	name = name.replace(/[[\]]/g, '\\$&');
+	const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
+	const results = regex.exec(hash);
+	if (!results) {
+		return null;
+	}
+	if (!results[2]) {
+		return '';
+	}
+	return decodeURIComponent(results[2].replace(/\+/g, ' '));
+};


Test patch:
diff --git a/loaders/utils/__tests__/__snapshots__/getSections.spec.js.snap b/loaders/utils/__tests__/__snapshots__/getSections.spec.js.snap
index c3401eb4..0b27d102 100644
--- a/loaders/utils/__tests__/__snapshots__/getSections.spec.js.snap
+++ b/loaders/utils/__tests__/__snapshots__/getSections.spec.js.snap
@@ -8,10 +8,13 @@ Array [
       "require": "!!~/loaders/examples-loader.js!~/test/components/Button/Readme.md",
     },
     "description": undefined,
+    "exampleMode": "collapse",
     "filepath": "components/Button/Readme.md",
     "name": "Readme",
+    "sectionDepth": 0,
     "sections": Array [],
     "slug": "readme-1",
+    "usageMode": "collapse",
   },
   Object {
     "components": Array [
@@ -85,10 +88,13 @@ Array [
     ],
     "content": undefined,
     "description": undefined,
+    "exampleMode": "collapse",
     "filepath": undefined,
     "name": "Components",
+    "sectionDepth": 0,
     "sections": Array [],
     "slug": "components-1",
+    "usageMode": "collapse",
   },
   Object {
     "components": Array [
@@ -149,10 +155,13 @@ Array [
     ],
     "content": undefined,
     "description": undefined,
+    "exampleMode": "collapse",
     "filepath": undefined,
     "name": "Ignore",
+    "sectionDepth": 0,
     "sections": Array [],
     "slug": "ignore-1",
+    "usageMode": "collapse",
   },
 ]
 `;
@@ -230,10 +239,13 @@ Object {
   ],
   "content": undefined,
   "description": undefined,
+  "exampleMode": "collapse",
   "filepath": undefined,
   "name": "Components",
+  "sectionDepth": 0,
   "sections": Array [],
   "slug": "components",
+  "usageMode": "collapse",
 }
 `;
 
@@ -244,10 +256,13 @@ Object {
     "require": "!!~/loaders/examples-loader.js!~/test/components/Button/Readme.md",
   },
   "description": undefined,
+  "exampleMode": "collapse",
   "filepath": "components/Button/Readme.md",
   "name": "Readme",
+  "sectionDepth": 0,
   "sections": Array [],
   "slug": "readme",
+  "usageMode": "collapse",
 }
 `;
 
@@ -311,9 +326,12 @@ Object {
   ],
   "content": undefined,
   "description": undefined,
+  "exampleMode": "collapse",
   "filepath": undefined,
   "name": "Ignore",
+  "sectionDepth": 0,
   "sections": Array [],
   "slug": "ignore",
+  "usageMode": "collapse",
 }
 `;
diff --git a/loaders/utils/__tests__/getSections.spec.js b/loaders/utils/__tests__/getSections.spec.js
index 346d4d55..91c94c1a 100644
--- a/loaders/utils/__tests__/getSections.spec.js
+++ b/loaders/utils/__tests__/getSections.spec.js
@@ -2,6 +2,13 @@ import path from 'path';
 import getSections, { processSection } from '../getSections';
 
 const configDir = path.resolve(__dirname, '../../../test');
+const config = {
+	configDir,
+	exampleMode: 'collapse',
+	usageMode: 'collapse',
+	getExampleFilename: a => a,
+	getComponentPathLine: a => a,
+};
 const sections = [
 	{
 		name: 'Readme',
@@ -17,11 +24,59 @@ const sections = [
 		ignore: '**/components/Annotation/*',
 	},
 ];
-const config = {
-	configDir,
-	getExampleFilename: a => a,
-	getComponentPathLine: a => a,
-};
+const sectionsWithDepth = [
+	{
+		name: 'Documentation',
+		sections: [
+			{
+				name: 'Files',
+				sections: [
+					{
+						name: 'First File',
+					},
+				],
+			},
+		],
+		sectionDepth: 2,
+	},
+	{
+		name: 'Components',
+		sections: [
+			{
+				name: 'Buttons',
+			},
+		],
+		sectionDepth: 0,
+	},
+];
+const sectionsWithBadDepth = [
+	{
+		name: 'Documentation',
+		sections: [
+			{
+				name: 'Files',
+				sections: [
+					{
+						name: 'First File',
+					},
+				],
+				sectionDepth: 2,
+			},
+		],
+	},
+];
+
+function filterSectionDepth(section) {
+	if (section.sections && section.sections.length) {
+		return {
+			sectionDepth: section.sectionDepth,
+			sections: section.sections.map(filterSectionDepth),
+		};
+	}
+	return {
+		sectionDepth: section.sectionDepth,
+	};
+}
 
 it('processSection() should return an object for section with content', () => {
 	const result = processSection(sections[0], config);
@@ -52,3 +107,51 @@ it('getSections() should return an array', () => {
 
 	expect(result).toMatchSnapshot();
 });
+
+it('getSections() should return an array of sectionsWithDepth with sectionDepth decreasing', () => {
+	const result = getSections(sectionsWithDepth, config);
+
+	expect(result.map(filterSectionDepth)).toEqual([
+		{
+			sectionDepth: 2,
+			sections: [
+				{
+					sectionDepth: 1,
+					sections: [
+						{
+							sectionDepth: 0,
+						},
+					],
+				},
+			],
+		},
+		{
+			sectionDepth: 0,
+			sections: [
+				{
+					sectionDepth: 0,
+				},
+			],
+		},
+	]);
+});
+
+it('getSections() should return an array of sectionsWithBadDepth taking the sectionDepth of the first depth of the sections', () => {
+	const result = getSections(sectionsWithBadDepth, config);
+
+	expect(result.map(filterSectionDepth)).toEqual([
+		{
+			sectionDepth: 0,
+			sections: [
+				{
+					sectionDepth: 0,
+					sections: [
+						{
+							sectionDepth: 0,
+						},
+					],
+				},
+			],
+		},
+	]);
+});
diff --git a/src/utils/__tests__/__snapshots__/getRouteData.spec.js.snap b/src/utils/__tests__/__snapshots__/getRouteData.spec.js.snap
index 9f866ecf..390afc09 100644
--- a/src/utils/__tests__/__snapshots__/getRouteData.spec.js.snap
+++ b/src/utils/__tests__/__snapshots__/getRouteData.spec.js.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`getRouteData should return first component if pagePerSection and hash is empty 1`] = `
+exports[`getRouteData should return first section if pagePerSection and hash is empty 1`] = `
 Object {
   "displayMode": "all",
   "sections": Array [
@@ -17,54 +17,28 @@ Object {
             ],
           },
         },
+        Object {
+          "module": 2,
+          "name": "Image",
+          "props": Object {
+            "displayName": "Image",
+          },
+        },
       ],
+      "exampleMode": "collapse",
+      "name": "Components",
+      "sectionDepth": 0,
       "sections": Array [],
+      "usageMode": "collapse",
     },
   ],
 }
 `;
 
-exports[`getRouteData should return first section if pagePerSection and hash is empty 1`] = `
+exports[`getRouteData should return not found if pagePerSection and hash is #/Buttons/Label/Not 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 [],
-        },
-      ],
-    },
-  ],
+  "displayMode": "notFound",
+  "sections": Array [],
 }
 `;
 
@@ -86,6 +60,26 @@ Object {
           },
         },
       ],
+      "exampleMode": "collapse",
+      "usageMode": "collapse",
+    },
+  ],
+}
+`;
+
+exports[`getRouteData should return one component if pagePerSection and hash is #/Buttons/Label 1`] = `
+Object {
+  "displayMode": "all",
+  "sections": Array [
+    Object {
+      "components": Array [
+        Object {
+          "module": 1,
+          "name": "Label",
+        },
+      ],
+      "exampleMode": "collapse",
+      "usageMode": "collapse",
     },
   ],
 }
@@ -108,6 +102,8 @@ Object {
           },
         },
       ],
+      "exampleMode": "collapse",
+      "usageMode": "collapse",
     },
   ],
 }
@@ -122,8 +118,11 @@ Object {
       "content": Array [
         "example 1",
       ],
+      "exampleMode": "collapse",
       "name": "Section",
+      "sectionDepth": 0,
       "sections": Array [],
+      "usageMode": "collapse",
     },
   ],
 }
@@ -139,8 +138,47 @@ Object {
         "example 0",
         "example 1",
       ],
+      "exampleMode": "collapse",
+      "name": "Section",
+      "sectionDepth": 0,
+      "sections": Array [],
+      "usageMode": "collapse",
+    },
+  ],
+}
+`;
+
+exports[`getRouteData should return one section if pagePerSection and hash is #/Section 1`] = `
+Object {
+  "displayMode": "all",
+  "sections": Array [
+    Object {
+      "components": Array [],
+      "content": Array [
+        "example 0",
+        "example 1",
+      ],
+      "exampleMode": "collapse",
       "name": "Section",
+      "sectionDepth": 0,
+      "sections": Array [],
+      "usageMode": "collapse",
+    },
+  ],
+}
+`;
+
+exports[`getRouteData should return one section without components if pagePerSection and hash is #/Buttons 1`] = `
+Object {
+  "displayMode": "all",
+  "sections": Array [
+    Object {
+      "components": Array [],
+      "exampleMode": "collapse",
+      "name": "Buttons",
+      "sectionDepth": 2,
       "sections": Array [],
+      "usageMode": "collapse",
     },
   ],
 }
diff --git a/src/utils/__tests__/__snapshots__/renderStyleguide.spec.js.snap b/src/utils/__tests__/__snapshots__/renderStyleguide.spec.js.snap
index a2416be1..28c832be 100644
--- a/src/utils/__tests__/__snapshots__/renderStyleguide.spec.js.snap
+++ b/src/utils/__tests__/__snapshots__/renderStyleguide.spec.js.snap
@@ -31,7 +31,7 @@ exports[`renderStyleguide should render StyleGuide component 1`] = `
           },
         ]
       }
-      useIsolatedLinks={undefined}
+      useRouterLinks={false}
     />
   }
 >
diff --git a/src/utils/__tests__/filterComponentsInSectionsByExactName.spec.js b/src/utils/__tests__/filterComponentsInSectionsByExactName.spec.js
index ab7905a8..89dbd560 100644
--- a/src/utils/__tests__/filterComponentsInSectionsByExactName.spec.js
+++ b/src/utils/__tests__/filterComponentsInSectionsByExactName.spec.js
@@ -22,7 +22,7 @@ const sections = deepfreeze([
 
 describe('filterComponentsInSectionsByExactName', () => {
 	it('should return components at any level with exact name', () => {
-		const result = filterComponentsInSectionsByExactName(sections, 'Image');
-		expect(result.map(x => x.name)).toEqual(['Image']);
+		const result = filterComponentsInSectionsByExactName(sections, 'Image', true);
+		expect(result[0].components.map(x => x.name)).toEqual(['Image']);
 	});
 });
diff --git a/src/utils/__tests__/getInfoFromHash.spec.js b/src/utils/__tests__/getInfoFromHash.spec.js
index c2e17fdf..e92f7cde 100644
--- a/src/utils/__tests__/getInfoFromHash.spec.js
+++ b/src/utils/__tests__/getInfoFromHash.spec.js
@@ -3,7 +3,12 @@ import getInfoFromHash from '../getInfoFromHash';
 describe('getInfoFromHash', () => {
 	it('should return important part of hash if it contains component name', () => {
 		const result = getInfoFromHash('#!/Button');
-		expect(result).toEqual({ targetName: 'Button', targetIndex: undefined });
+		expect(result).toEqual({
+			isolate: true,
+			hashArray: ['Button'],
+			targetName: 'Button',
+			targetIndex: undefined,
+		});
 	});
 
 	it('should return an empty object if hash contains no component name', () => {
@@ -13,6 +18,21 @@ describe('getInfoFromHash', () => {
 
 	it('should return the decoded targetName when router name is not English such as Chinese', () => {
 		const result = getInfoFromHash('#!/Api%20%E7%BB%84%E4%BB%B6');
-		expect(result).toEqual({ targetName: 'Api 组件', targetIndex: undefined });
+		expect(result).toEqual({
+			isolate: true,
+			hashArray: ['Api 组件'],
+			targetName: 'Api 组件',
+			targetIndex: undefined,
+		});
+	});
+
+	it('hashArray should return an array of each deep and isolate should return false when the url starts with #/', () => {
+		const result = getInfoFromHash('#/Documentation/Files/Buttons');
+		expect(result).toEqual({
+			isolate: false,
+			hashArray: ['Documentation', 'Files', 'Buttons'],
+			targetName: 'Documentation',
+			targetIndex: undefined,
+		});
 	});
 });
diff --git a/src/utils/__tests__/getPageTitle.spec.js b/src/utils/__tests__/getPageTitle.spec.js
index 37b3cb55..14bda24b 100644
--- a/src/utils/__tests__/getPageTitle.spec.js
+++ b/src/utils/__tests__/getPageTitle.spec.js
@@ -20,6 +20,16 @@ describe('getPageTitle', () => {
 		expect(result).toMatch(name);
 	});
 
+	it('should return section name for example isolation mode of a example content', () => {
+		const sectionName = 'Section';
+		const result = getPageTitle(
+			[{ name: sectionName, content: [], components: [] }],
+			baseTitle,
+			'example'
+		);
+		expect(result).toMatch(sectionName);
+	});
+
 	it('should return section name for example isolation mode, if no components are passed', () => {
 		const name = 'Section';
 		const result = getPageTitle([{ name }], baseTitle, 'example');
@@ -31,4 +41,10 @@ describe('getPageTitle', () => {
 		const result = getPageTitle([{ name }], baseTitle, 'section');
 		expect(result).toMatch(name);
 	});
+
+	it('should return Error 404 for notFound isolation mode', () => {
+		const name = 'Section';
+		const result = getPageTitle([{ name }], baseTitle, 'notFound');
+		expect(result).toMatch('Page not found');
+	});
 });
diff --git a/src/utils/__tests__/getRouteData.spec.js b/src/utils/__tests__/getRouteData.spec.js
index 7f37694b..39d50223 100644
--- a/src/utils/__tests__/getRouteData.spec.js
+++ b/src/utils/__tests__/getRouteData.spec.js
@@ -6,6 +6,7 @@ const sections = deepfreeze([
 	{
 		sections: [
 			{
+				name: 'Components',
 				components: [
 					{
 						name: 'Button',
@@ -24,12 +25,35 @@ const sections = deepfreeze([
 					},
 				],
 				sections: [],
+				exampleMode: 'collapse',
+				usageMode: 'collapse',
+				sectionDepth: 0,
 			},
 			{
 				name: 'Section',
 				content: ['example 0', 'example 1'],
 				components: [],
 				sections: [],
+				exampleMode: 'collapse',
+				usageMode: 'collapse',
+				sectionDepth: 0,
+			},
+			{
+				name: 'Buttons',
+				components: [
+					{
+						name: 'Label',
+						module: 1,
+					},
+					{
+						name: 'RandomButton',
+						module: 2,
+					},
+				],
+				sections: [],
+				exampleMode: 'collapse',
+				usageMode: 'collapse',
+				sectionDepth: 2,
 			},
 		],
 	},
@@ -62,12 +86,27 @@ describe('getRouteData', () => {
 	});
 
 	it('should return first section if pagePerSection and hash is empty', () => {
-		const result = getRouteData(sections, '', true);
+		const result = getRouteData(sections[0].sections, '', true);
 		expect(result).toMatchSnapshot();
 	});
 
-	it('should return first component if pagePerSection and hash is empty', () => {
-		const result = getRouteData(sections[0].sections, '', true);
+	it('should return one section if pagePerSection and hash is #/Section', () => {
+		const result = getRouteData(sections, '#/Section', true);
+		expect(result).toMatchSnapshot();
+	});
+
+	it('should return one section without components if pagePerSection and hash is #/Buttons', () => {
+		const result = getRouteData(sections, '#/Buttons', true);
+		expect(result).toMatchSnapshot();
+	});
+
+	it('should return one component if pagePerSection and hash is #/Buttons/Label', () => {
+		const result = getRouteData(sections, '#/Buttons/Label', true);
+		expect(result).toMatchSnapshot();
+	});
+
+	it('should return not found if pagePerSection and hash is #/Buttons/Label/Not', () => {
+		const result = getRouteData(sections, '#/Buttons/Label/Not', true);
 		expect(result).toMatchSnapshot();
 	});
 });
diff --git a/src/utils/__tests__/getUrl.spec.js b/src/utils/__tests__/getUrl.spec.js
index a8d8feff..f8a5fc33 100644
--- a/src/utils/__tests__/getUrl.spec.js
+++ b/src/utils/__tests__/getUrl.spec.js
@@ -4,6 +4,7 @@ describe('getUrl', () => {
 	const loc = {
 		origin: 'http://example.com',
 		pathname: '/styleguide/',
+		hash: '#/Components',
 	};
 	const name = 'FooBar';
 	const slug = 'foobar';
@@ -62,4 +63,19 @@ describe('getUrl', () => {
 		const result = getUrl({ name, slug, nochrome: true, absolute: true }, loc);
 		expect(result).toBe('http://example.com/styleguide/?nochrome#!/FooBar');
 	});
+
+	it('should return a route path', () => {
+		const result = getUrl({ name, slug, hashPath: ['Documentation'] }, loc);
+		expect(result).toBe('/styleguide/#/Documentation/FooBar');
+	});
+
+	it('should return a route path with a param id=foobar', () => {
+		const result = getUrl({ name, slug, hashPath: ['Documentation'], id: true }, loc);
+		expect(result).toBe('/styleguide/#/Documentation?id=foobar');
+	});
+
+	it('should return a param id=foobar', () => {
+		const result = getUrl({ name, slug, takeHash: true, id: true }, loc);
+		expect(result).toBe('/styleguide/#/Components?id=foobar');
+	});
 });
diff --git a/src/utils/__tests__/handleHash.spec.js b/src/utils/__tests__/handleHash.spec.js
new file mode 100644
index 00000000..2cf138a0
--- /dev/null
+++ b/src/utils/__tests__/handleHash.spec.js
@@ -0,0 +1,66 @@
+import { hasInHash, getHash, getHashAsArray, getParameterByName } from '../handleHash';
+
+describe('handleHash', () => {
+	const isolateHash = '#!/';
+	const routeHash = '#/';
+
+	it('hasInHash should return true if has #!/', () => {
+		const result = hasInHash('#!/FooBar', isolateHash);
+		expect(result).toBe(true);
+	});
+
+	it('hasInHash should return false if does not have #!/', () => {
+		const result = hasInHash('#/FooBar', isolateHash);
+		expect(result).toBe(false);
+	});
+
+	it('hasInHash should return true if has #/', () => {
+		const result = hasInHash('#/FooBar', routeHash);
+		expect(result).toBe(true);
+	});
+
+	it('hasInHash should return false if does not have #/', () => {
+		const result = hasInHash('#!/FooBar', routeHash);
+		expect(result).toBe(false);
+	});
+
+	it('getHash should return FooBar', () => {
+		const result = getHash('#/FooBar', routeHash);
+		expect(result).toBe('FooBar');
+	});
+
+	it('getHash should return FooBar without params', () => {
+		const result = getHash('#/FooBar?id=Example/Perfect', routeHash);
+		expect(result).toBe('FooBar');
+	});
+
+	it('getHash should return decode value', () => {
+		const result = getHash('#!/Api%20%E7%BB%84%E4%BB%B6', isolateHash);
+		expect(result).toBe('Api 组件');
+	});
+
+	it('getHashAsArray should return array', () => {
+		const result = getHashAsArray('#!/FooBar/Component', isolateHash);
+		expect(result).toEqual(['FooBar', 'Component']);
+	});
+
+	it('getHashAsArray should return array without params', () => {
+		const result = getHashAsArray('#/FooBar/Component?id=Example/Perfect', routeHash);
+		expect(result).toEqual(['FooBar', 'Component']);
+	});
+
+	it('getParameterByName should return Example when has id param', () => {
+		const result = getParameterByName('#/FooBar/Component?id=Example', 'id');
+		expect(result).toBe('Example');
+	});
+
+	it('getParameterByName should return null when do not has params', () => {
+		const result = getParameterByName('#/FooBar/Component', 'id');
+		expect(result).toEqual(null);
+	});
+
+	it('getParameterByName should return null when do not has id params', () => {
+		const result = getParameterByName('#/FooBar/Component?foobar=3', 'id');
+		expect(result).toEqual(null);
+	});
+});
diff --git a/src/utils/__tests__/renderStyleguide.spec.js b/src/utils/__tests__/renderStyleguide.spec.js
index 3ed7dfba..391d8a7d 100644
--- a/src/utils/__tests__/renderStyleguide.spec.js
+++ b/src/utils/__tests__/renderStyleguide.spec.js
@@ -3,6 +3,7 @@ import renderStyleguide from '../renderStyleguide';
 const styleguide = {
 	config: {
 		title: 'My Style Guide',
+		pagePerSection: false,
 	},
 	sections: [
 		{