diff --git a/libs/hooks.js b/libs/hooks.js
new file mode 100644
index 0000000000000000000000000000000000000000..a6b01a12b278e248b6017c889aad5678ba9547b4
--- /dev/null
+++ b/libs/hooks.js
@@ -0,0 +1,77 @@
+export default {
+  'content:file:beforeParse': (file) => {
+    // Netlifycms cannot create array in json file at root level and nuxt-content need an array. So, ressources.json is parsed to fetch 'ressources' key.
+    if (file.extension === '.json' && /.*ressources.json$/.test(file.path)) {
+      file.data = JSON.stringify(JSON.parse(file.data).ressources)
+    }
+
+    // Split md file with `---`. Used for flat plan.
+    if (file.extension === '.md' && /^---.*layout:.*---/s.test(file.data)) {
+      let t = 0
+      const data = file.data.replace(/---/g, (match) => {
+        ++t
+        if (t === 2) return '---\n<section>'
+        if (t > 2) return '</section>\n<section>'
+        return match
+      })
+      file.data = data + '\n</section>'
+    }
+  },
+  async 'content:file:beforeInsert'(document) {
+    // Routes all /pages/_slug to /_slug
+    if (document.path.startsWith('/pages/')) {
+      document.dir = '/'
+      document.path = document.path.replace('/pages/', '/')
+    }
+
+    if (document.extension === '.md') {
+      /*
+       * Add reading time
+       */
+      document.readingTime = require('reading-time')(document.text)
+
+      /*
+       * Add lexique terms and check if terms exist
+       */
+      // Search lexique terms
+      const lexiqueTerms = [
+        ...document.text.matchAll(
+          /<lexique[>]([^<]*)<\/|<lexique\s+title="(.*)"/g
+        ),
+      ].map((match) => {
+        return (match[2] || match[1]).toLowerCase() // Priority for term in title=""
+      })
+
+      if (lexiqueTerms.length) {
+        // Add terms to document
+        document.lexiqueTerms = [...new Set(lexiqueTerms)] // remove duplicate
+
+        const { $content } = require('@nuxt/content')
+        // Check if term exists
+        if ($content) {
+          for (let i = 0; i < document.lexiqueTerms.length; i++) {
+            const term = document.lexiqueTerms[i]
+            const lexique = await $content('lexique')
+              .where({
+                $or: [
+                  { title: { $regex: [`^${term}$`, 'i'] } }, // case insensitive
+                  { synonyms: { $contains: term } }, // search in synonyms
+                ],
+              })
+              .limit(1)
+              .fetch()
+
+            if (!lexique.length) {
+              // eslint-disable-next-line no-console
+              console.warn(
+                `Term "${term}" in file ${
+                  document.path + document.extension
+                } doesn't exist`
+              )
+            }
+          }
+        }
+      }
+    }
+  },
+}
diff --git a/libs/icons.js b/libs/icons.js
new file mode 100644
index 0000000000000000000000000000000000000000..62b317e92a80a5fc85e39775b3bab7cfa49d2b65
--- /dev/null
+++ b/libs/icons.js
@@ -0,0 +1,66 @@
+export default {
+  regular: ['faClock'],
+  solid: [
+    'faHome',
+    'faSearch',
+    'faSkullCrossbones',
+    'faChevronLeft',
+    'faChevronRight',
+    'faArrowLeft',
+    'faArrowRight',
+    'faSun',
+    'faMoon',
+    'faHeart',
+    'faInfoCircle',
+    'faCheckCircle',
+    'faCheck',
+    'faExclamationCircle',
+    'faExclamationTriangle',
+    'faExternalLinkAlt',
+    'faBars',
+    'faUserCircle',
+    'faGlobe',
+    'faAngry',
+    'faCompressArrowsAlt',
+    'faExpandArrowsAlt',
+    'faFont',
+    'faWheelchair',
+    'faCaretDown',
+  ],
+  brands: [
+    'faCreativeCommonsNcEu',
+    'faDiaspora',
+    'faDiscourse',
+    'faGithub',
+    'faGitlab',
+    'faYoutube',
+    'faRocketchat',
+    'faFacebook',
+    'faTwitter',
+    'faDiscord',
+    'faApple',
+    'faAndroid',
+    'faBitcoin',
+    'faChrome',
+    'faFirefox',
+    'faCodepen',
+    'faDev',
+    'faDocker',
+    'faDropbox',
+    'faMastodon',
+    'faMedium',
+    'faNpm',
+    'faReddit',
+    'faSlack',
+    'faSoundcloud',
+    'faSpotify',
+    'faSteam',
+    'faTeamspeak',
+    'faTelegram',
+    'faTrello',
+    'faVimeo',
+    'faWhatsapp',
+    'faWikipediaW',
+    'faWordpress',
+  ],
+}
diff --git a/nuxt.config.js b/nuxt.config.js
index 8cdb8513db1082c68a9d1913bb97261629a21354..b3ac3b2d4c53b9c3ca873b9ce10079dfe0b9c499 100644
--- a/nuxt.config.js
+++ b/nuxt.config.js
@@ -1,5 +1,7 @@
 import i18n from './plugins/i18n.js'
 import config from './static/settings/globals.json'
+import hooks from './libs/hooks.js'
+import icons from './libs/icons.js'
 
 export default {
   target: 'static',
@@ -66,72 +68,7 @@ export default {
       {
         component: 'fa', // component name
         addCss: false,
-        icons: {
-          regular: ['faClock'],
-          solid: [
-            'faHome',
-            'faSearch',
-            'faSkullCrossbones',
-            'faChevronLeft',
-            'faChevronRight',
-            'faArrowLeft',
-            'faArrowRight',
-            'faSun',
-            'faMoon',
-            'faHeart',
-            'faInfoCircle',
-            'faCheckCircle',
-            'faCheck',
-            'faExclamationCircle',
-            'faExclamationTriangle',
-            'faExternalLinkAlt',
-            'faBars',
-            'faUserCircle',
-            'faGlobe',
-            'faAngry',
-            'faCompressArrowsAlt',
-            'faExpandArrowsAlt',
-            'faFont',
-            'faWheelchair',
-            'faCaretDown',
-          ],
-          brands: [
-            'faCreativeCommonsNcEu',
-            'faDiaspora',
-            'faDiscourse',
-            'faGithub',
-            'faGitlab',
-            'faYoutube',
-            'faRocketchat',
-            'faFacebook',
-            'faTwitter',
-            'faDiscord',
-            'faApple',
-            'faAndroid',
-            'faBitcoin',
-            'faChrome',
-            'faFirefox',
-            'faCodepen',
-            'faDev',
-            'faDocker',
-            'faDropbox',
-            'faMastodon',
-            'faMedium',
-            'faNpm',
-            'faReddit',
-            'faSlack',
-            'faSoundcloud',
-            'faSpotify',
-            'faSteam',
-            'faTeamspeak',
-            'faTelegram',
-            'faTrello',
-            'faVimeo',
-            'faWhatsapp',
-            'faWikipediaW',
-            'faWordpress',
-          ],
-        },
+        icons,
       },
     ],
   ],
@@ -180,11 +117,6 @@ export default {
     },
   },
 
-  // https://github.com/Chantouch/nuxt-clipboard
-  clipboard: {
-    autoSetContainer: true,
-  },
-
   // https://content.nuxtjs.org/fr/configuration
   content: {
     markdown: {
@@ -199,80 +131,16 @@ export default {
     classSuffix: '',
   },
 
+  // https://github.com/Chantouch/nuxt-clipboard
+  clipboard: {
+    autoSetContainer: true,
+  },
+
   /**
    * Nuxt hooks
    * https://nuxtjs.org/docs/2.x/configuration-glossary/configuration-hooks
    */
-  hooks: {
-    // Netlifycms cannot create array in json file at root level and nuxt-content need an array. So, ressources.json is parsed to fetch 'ressources' key.
-    'content:file:beforeParse': (file) => {
-      if (file.extension === '.json' && /.*ressources.json$/.test(file.path)) {
-        file.data = JSON.stringify(JSON.parse(file.data).ressources)
-      } else if (file.extension === '.md') {
-        if (/^---.*layout:.*---/s.test(file.data)) {
-          let t = 0
-          const data = file.data.replace(/---/g, (match) => {
-            ++t
-            if (t === 2) return '---\n<section>'
-            if (t > 2) return '</section>\n<section>'
-            return match
-          })
-          file.data = data + '\n</section>'
-        }
-      }
-    },
-    async 'content:file:beforeInsert'(document) {
-      if (document.extension === '.md') {
-        /*
-         * Add reading time
-         */
-        document.readingTime = require('reading-time')(document.text)
-
-        /*
-         * Add lexique terms and check if terms exist
-         */
-        // Search lexique terms
-        const lexiqueTerms = [
-          ...document.text.matchAll(
-            /<lexique[>]([^<]*)<\/|<lexique\s+title="(.*)"/g
-          ),
-        ].map((match) => {
-          return (match[2] || match[1]).toLowerCase() // Priority for term in title=""
-        })
-
-        if (lexiqueTerms.length) {
-          // Add terms to document
-          document.lexiqueTerms = [...new Set(lexiqueTerms)] // remove duplicate
-
-          const { $content } = require('@nuxt/content')
-          // Check if term exists
-          if ($content) {
-            for (let i = 0; i < document.lexiqueTerms.length; i++) {
-              const term = document.lexiqueTerms[i]
-              const lexique = await $content('lexique')
-                .where({
-                  $or: [
-                    { title: { $regex: [`^${term}$`, 'i'] } }, // case insensitive
-                    { synonyms: { $contains: term } }, // search in synonyms
-                  ],
-                })
-                .limit(1)
-                .fetch()
-
-              if (!lexique.length) {
-                // eslint-disable-next-line no-console
-                console.warn(
-                  `Term "${term}" in file ${
-                    document.path + document.extension
-                  } doesn't exist`
-                )
-              }
-            }
-          }
-        }
-      }
-    },
-  },
+  hooks,
 
   /**
    * Build configuration