diff --git a/front/src/components/auth/Login.vue b/front/src/components/auth/Login.vue
deleted file mode 100644
index 20aff3236bd797e0c03a50ce9ec36b29287e20c9..0000000000000000000000000000000000000000
--- a/front/src/components/auth/Login.vue
+++ /dev/null
@@ -1,122 +0,0 @@
-<template>
-  <main class="main pusher" v-title="labels.title">
-    <section class="ui vertical stripe segment">
-      <div class="ui small text container">
-        <h2><translate translate-context="Content/Login/Title/Verb">Log in to your Funkwhale account</translate></h2>
-        <form class="ui form" @submit.prevent="submit()">
-          <div v-if="error" class="ui negative message">
-            <div class="header"><translate translate-context="Content/Login/Error message.Title">We cannot log you in</translate></div>
-            <ul class="list">
-              <li v-if="error == 'invalid_credentials'"><translate translate-context="Content/Login/Error message.List item/Call to action">Please double-check your username/password couple is correct</translate></li>
-              <li v-else><translate translate-context="Content/Login/Error message/List item">An unknown error happend, this can mean the server is down or cannot be reached</translate></li>
-            </ul>
-          </div>
-          <div class="field">
-            <label>
-              <translate translate-context="Content/Login/Input.Label/Noun">Username or email</translate> |
-              <router-link :to="{path: '/signup'}">
-                <translate translate-context="*/Signup/Link/Verb">Create an account</translate>
-              </router-link>
-            </label>
-            <input
-            ref="username"
-            tabindex="1"
-            required
-            name="username"
-            type="text"
-            autofocus
-            :placeholder="labels.usernamePlaceholder"
-            v-model="credentials.username"
-            >
-          </div>
-          <div class="field">
-            <label>
-              <translate translate-context="Content/*/Input.Label">Password</translate> |
-              <router-link :to="{name: 'auth.password-reset', query: {email: credentials.username}}">
-                <translate translate-context="*/Login/*/Verb">Reset your password</translate>
-              </router-link>
-            </label>
-            <password-input :index="2" required v-model="credentials.password" />
-
-          </div>
-          <button tabindex="3" :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit">
-             <translate translate-context="*/Login/*/Verb">Login</translate>
-          </button>
-        </form>
-      </div>
-    </section>
-  </main>
-</template>
-
-<script>
-import PasswordInput from "@/components/forms/PasswordInput"
-
-export default {
-  props: {
-    next: { type: String, default: "/library" }
-  },
-  components: {
-    PasswordInput
-  },
-  data() {
-    return {
-      // We need to initialize the component with any
-      // properties that will be used in it
-      credentials: {
-        username: "",
-        password: ""
-      },
-      error: "",
-      isLoading: false
-    }
-  },
-  created () {
-    if (this.$store.state.auth.authenticated) {
-      this.$router.push(this.next)
-    }
-  },
-  mounted() {
-    this.$refs.username.focus()
-  },
-  computed: {
-    labels() {
-      let usernamePlaceholder = this.$pgettext('Content/Login/Input.Placeholder', "Enter your username or email")
-      let title = this.$pgettext('Head/Login/Title', "Log In")
-      return {
-        usernamePlaceholder,
-        title
-      }
-    }
-  },
-  methods: {
-    submit() {
-      var self = this
-      self.isLoading = true
-      this.error = ""
-      var credentials = {
-        username: this.credentials.username,
-        password: this.credentials.password
-      }
-      this.$store
-        .dispatch("auth/login", {
-          credentials,
-          next: this.next,
-          onError: error => {
-            if (error.response.status === 400) {
-              self.error = "invalid_credentials"
-            } else {
-              self.error = "unknown_error"
-            }
-          }
-        })
-        .then(e => {
-          self.isLoading = false
-        })
-    }
-  }
-}
-</script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/auth/LoginForm.vue b/front/src/components/auth/LoginForm.vue
new file mode 100644
index 0000000000000000000000000000000000000000..e21b33d041432d8e49bdc69070d46645020ff9b1
--- /dev/null
+++ b/front/src/components/auth/LoginForm.vue
@@ -0,0 +1,114 @@
+<template>
+  <form class="ui form" @submit.prevent="submit()">
+    <slot name="title"></slot>
+    <div v-if="error" class="ui negative message">
+      <div class="header"><translate translate-context="Content/Login/Error message.Title">We cannot log you in</translate></div>
+      <ul class="list">
+        <li v-if="error == 'invalid_credentials'"><translate translate-context="Content/Login/Error message.List item/Call to action">Please double-check your username/password couple is correct</translate></li>
+        <li v-else><translate translate-context="Content/Login/Error message/List item">An unknown error happend, this can mean the server is down or cannot be reached</translate></li>
+      </ul>
+    </div>
+    <div class="field">
+      <label>
+        <translate translate-context="Content/Login/Input.Label/Noun">Username or email</translate> |
+        <router-link :to="{path: '/signup'}">
+          <translate translate-context="*/Signup/Link/Verb">Create an account</translate>
+        </router-link>
+      </label>
+      <input
+      ref="username"
+      tabindex="1"
+      required
+      name="username"
+      type="text"
+      autofocus
+      :placeholder="labels.usernamePlaceholder"
+      v-model="credentials.username"
+      >
+    </div>
+    <div class="field">
+      <label>
+        <translate translate-context="Content/*/Input.Label">Password</translate> |
+        <router-link :to="{name: 'auth.password-reset', query: {email: credentials.username}}">
+          <translate translate-context="*/Login/*/Verb">Reset your password</translate>
+        </router-link>
+      </label>
+      <password-input :index="2" required v-model="credentials.password" />
+
+    </div>
+    <button tabindex="3" :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit">
+        <translate translate-context="*/Login/*/Verb">Login</translate>
+    </button>
+  </form>
+</template>
+
+<script>
+import PasswordInput from "@/components/forms/PasswordInput"
+
+export default {
+  props: {
+    next: { type: String, default: "/library" }
+  },
+  components: {
+    PasswordInput
+  },
+  data() {
+    return {
+      // We need to initialize the component with any
+      // properties that will be used in it
+      credentials: {
+        username: "",
+        password: ""
+      },
+      error: "",
+      isLoading: false
+    }
+  },
+  created () {
+    if (this.$store.state.auth.authenticated) {
+      this.$router.push(this.next)
+    }
+  },
+  mounted() {
+    this.$refs.username.focus()
+  },
+  computed: {
+    labels() {
+      let usernamePlaceholder = this.$pgettext('Content/Login/Input.Placeholder', "Enter your username or email")
+      return {
+        usernamePlaceholder,
+      }
+    }
+  },
+  methods: {
+    submit() {
+      var self = this
+      self.isLoading = true
+      this.error = ""
+      var credentials = {
+        username: this.credentials.username,
+        password: this.credentials.password
+      }
+      this.$store
+        .dispatch("auth/login", {
+          credentials,
+          next: this.next,
+          onError: error => {
+            if (error.response.status === 400) {
+              self.error = "invalid_credentials"
+            } else {
+              self.error = "unknown_error"
+            }
+          }
+        })
+        .then(e => {
+          self.isLoading = false
+        })
+    }
+  }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+</style>
diff --git a/front/src/components/auth/Signup.vue b/front/src/components/auth/Signup.vue
deleted file mode 100644
index 974d082bda98153009cc2e9f35cd4c0e67ab16fd..0000000000000000000000000000000000000000
--- a/front/src/components/auth/Signup.vue
+++ /dev/null
@@ -1,146 +0,0 @@
-<template>
-  <main class="main pusher" v-title="labels.title">
-    <section class="ui vertical stripe segment">
-      <div class="ui small text container">
-        <h2><translate translate-context="Content/Signup/Title">Create a funkwhale account</translate></h2>
-        <form
-          :class="['ui', {'loading': isLoadingInstanceSetting}, 'form']"
-          @submit.prevent="submit()">
-          <p class="ui message" v-if="!$store.state.instance.settings.users.registration_enabled.value">
-            <translate translate-context="Content/Signup/Form/Paragraph">Registration are closed on this instance, you will need an invitation code to signup.</translate>
-          </p>
-
-          <div v-if="errors.length > 0" class="ui negative message">
-            <div class="header"><translate translate-context="Content/Signup/Form/Paragraph">Your account cannot be created.</translate></div>
-            <ul class="list">
-              <li v-for="error in errors">{{ error }}</li>
-            </ul>
-          </div>
-          <div class="field">
-            <label><translate translate-context="Content/*/*">Username</translate></label>
-            <input
-            ref="username"
-            name="username"
-            required
-            type="text"
-            autofocus
-            :placeholder="labels.usernamePlaceholder"
-            v-model="username">
-          </div>
-          <div class="field">
-            <label><translate translate-context="Content/*/*/Noun">Email</translate></label>
-            <input
-            ref="email"
-            name="email"
-            required
-            type="email"
-            :placeholder="labels.emailPlaceholder"
-            v-model="email">
-          </div>
-          <div class="field">
-            <label><translate translate-context="Content/*/Input.Label">Password</translate></label>
-            <password-input v-model="password" />
-          </div>
-          <div class="field" v-if="!$store.state.instance.settings.users.registration_enabled.value">
-            <label><translate translate-context="Content/*/Input.Label">Invitation code</translate></label>
-            <input
-            required
-            type="text"
-            name="invitation"
-            :placeholder="labels.placeholder"
-            v-model="invitation">
-          </div>
-          <button :class="['ui', 'green', {'loading': isLoading}, 'button']" type="submit">
-            <translate translate-context="Content/Signup/Button.Label">Create my account</translate>
-          </button>
-        </form>
-      </div>
-    </section>
-  </main>
-</template>
-
-<script>
-import axios from "axios"
-import logger from "@/logging"
-
-import PasswordInput from "@/components/forms/PasswordInput"
-
-export default {
-  props: {
-    defaultInvitation: { type: String, required: false, default: null },
-    next: { type: String, default: "/" }
-  },
-  components: {
-    PasswordInput
-  },
-  data() {
-    return {
-      username: "",
-      email: "",
-      password: "",
-      isLoadingInstanceSetting: true,
-      errors: [],
-      isLoading: false,
-      invitation: this.defaultInvitation
-    }
-  },
-  created() {
-    let self = this
-    this.$store.dispatch("instance/fetchSettings", {
-      callback: function() {
-        self.isLoadingInstanceSetting = false
-      }
-    })
-  },
-  computed: {
-    labels() {
-      let title = this.$pgettext("*/Signup/Title", "Sign Up")
-      let placeholder = this.$pgettext(
-        "Content/Signup/Form/Placeholder",
-        "Enter your invitation code (case insensitive)"
-      )
-      let usernamePlaceholder = this.$pgettext("Content/Signup/Form/Placeholder", "Enter your username")
-      let emailPlaceholder = this.$pgettext("Content/Signup/Form/Placeholder", "Enter your email")
-      return {
-        title,
-        usernamePlaceholder,
-        emailPlaceholder,
-        placeholder
-      }
-    }
-  },
-  methods: {
-    submit() {
-      var self = this
-      self.isLoading = true
-      this.errors = []
-      var payload = {
-        username: this.username,
-        password1: this.password,
-        password2: this.password,
-        email: this.email,
-        invitation: this.invitation
-      }
-      return axios.post("auth/registration/", payload).then(
-        response => {
-          logger.default.info("Successfully created account")
-          self.$router.push({
-            name: "profile",
-            params: {
-              username: this.username
-            }
-          })
-        },
-        error => {
-          self.errors = error.backendErrors
-          self.isLoading = false
-        }
-      )
-    }
-  }
-}
-</script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/auth/SignupForm.vue b/front/src/components/auth/SignupForm.vue
new file mode 100644
index 0000000000000000000000000000000000000000..57c8a0c8de3479b5c3b61e6dbba501a198caa43a
--- /dev/null
+++ b/front/src/components/auth/SignupForm.vue
@@ -0,0 +1,134 @@
+<template>
+  <form
+    :class="['ui', {'loading': isLoadingInstanceSetting}, 'form']"
+    @submit.prevent="submit()">
+    <slot name="title"></slot>
+    <p class="ui message" v-if="!$store.state.instance.settings.users.registration_enabled.value">
+      <translate translate-context="Content/Signup/Form/Paragraph">Registration are closed on this instance, you will need an invitation code to signup.</translate>
+    </p>
+
+    <div v-if="errors.length > 0" class="ui negative message">
+      <div class="header"><translate translate-context="Content/Signup/Form/Paragraph">Your account cannot be created.</translate></div>
+      <ul class="list">
+        <li v-for="error in errors">{{ error }}</li>
+      </ul>
+    </div>
+    <div class="field">
+      <label><translate translate-context="Content/*/*">Username</translate></label>
+      <input
+      ref="username"
+      name="username"
+      required
+      type="text"
+      autofocus
+      :placeholder="labels.usernamePlaceholder"
+      v-model="username">
+    </div>
+    <div class="field">
+      <label><translate translate-context="Content/*/*/Noun">Email</translate></label>
+      <input
+      ref="email"
+      name="email"
+      required
+      type="email"
+      :placeholder="labels.emailPlaceholder"
+      v-model="email">
+    </div>
+    <div class="field">
+      <label><translate translate-context="Content/*/Input.Label">Password</translate></label>
+      <password-input v-model="password" />
+    </div>
+    <div class="field" v-if="!$store.state.instance.settings.users.registration_enabled.value">
+      <label><translate translate-context="Content/*/Input.Label">Invitation code</translate></label>
+      <input
+      required
+      type="text"
+      name="invitation"
+      :placeholder="labels.placeholder"
+      v-model="invitation">
+    </div>
+    <button :class="['ui', 'green', {'loading': isLoading}, 'button']" type="submit">
+      <translate translate-context="Content/Signup/Button.Label">Create my account</translate>
+    </button>
+  </form>
+</template>
+
+<script>
+import axios from "axios"
+import logger from "@/logging"
+
+import PasswordInput from "@/components/forms/PasswordInput"
+
+export default {
+  props: {
+    defaultInvitation: { type: String, required: false, default: null },
+    next: { type: String, default: "/" }
+  },
+  components: {
+    PasswordInput
+  },
+  data() {
+    return {
+      username: "",
+      email: "",
+      password: "",
+      isLoadingInstanceSetting: true,
+      errors: [],
+      isLoading: false,
+      invitation: this.defaultInvitation
+    }
+  },
+  created() {
+    let self = this
+    this.$store.dispatch("instance/fetchSettings", {
+      callback: function() {
+        self.isLoadingInstanceSetting = false
+      }
+    })
+  },
+  computed: {
+    labels() {
+      let placeholder = this.$pgettext(
+        "Content/Signup/Form/Placeholder",
+        "Enter your invitation code (case insensitive)"
+      )
+      let usernamePlaceholder = this.$pgettext("Content/Signup/Form/Placeholder", "Enter your username")
+      let emailPlaceholder = this.$pgettext("Content/Signup/Form/Placeholder", "Enter your email")
+      return {
+        usernamePlaceholder,
+        emailPlaceholder,
+        placeholder
+      }
+    }
+  },
+  methods: {
+    submit() {
+      var self = this
+      self.isLoading = true
+      this.errors = []
+      var payload = {
+        username: this.username,
+        password1: this.password,
+        password2: this.password,
+        email: this.email,
+        invitation: this.invitation
+      }
+      return axios.post("auth/registration/", payload).then(
+        response => {
+          logger.default.info("Successfully created account")
+          self.$router.push({
+            name: "profile",
+            params: {
+              username: this.username
+            }
+          })
+        },
+        error => {
+          self.errors = error.backendErrors
+          self.isLoading = false
+        }
+      )
+    }
+  }
+}
+</script>
diff --git a/front/src/router/index.js b/front/src/router/index.js
index 6a9ba6112f7680766b22945bfd41097db3f143aa..ed46cc5944bcd7069763cb210c5e15b039ab6540 100644
--- a/front/src/router/index.js
+++ b/front/src/router/index.js
@@ -30,7 +30,7 @@ export default new Router({
       path: '/login',
       name: 'login',
       component: () =>
-        import(/* webpackChunkName: "core" */ "@/components/auth/Login"),
+        import(/* webpackChunkName: "core" */ "@/views/auth/Login"),
       props: (route) => ({ next: route.query.next || '/library' })
     },
     {
@@ -85,7 +85,7 @@ export default new Router({
       path: '/signup',
       name: 'signup',
       component: () =>
-        import(/* webpackChunkName: "core" */ "@/components/auth/Signup"),
+        import(/* webpackChunkName: "core" */ "@/views/auth/Signup"),
       props: (route) => ({
         defaultInvitation: route.query.invitation
       })
diff --git a/front/src/views/auth/Login.vue b/front/src/views/auth/Login.vue
new file mode 100644
index 0000000000000000000000000000000000000000..07d796131d63b1202c3a7ba502b8cb55a4e47eb5
--- /dev/null
+++ b/front/src/views/auth/Login.vue
@@ -0,0 +1,32 @@
+<template>
+  <main class="main pusher" v-title="labels.title">
+    <section class="ui vertical stripe segment">
+      <div class="ui small text container">
+        <h2 slot="title"><translate translate-context="Content/Login/Title/Verb">Log in to your Funkwhale account</translate></h2>
+        <login-form :next="next"></login-form>
+      </div>
+    </section>
+  </main>
+</template>
+
+<script>
+import LoginForm from "@/components/auth/LoginForm"
+import PasswordInput from "@/components/forms/PasswordInput"
+
+export default {
+  components: {
+    LoginForm
+  },
+  props: {
+    next: { type: String, default: "/library" }
+  },
+  computed: {
+    labels() {
+      let title = this.$pgettext('Head/Login/Title', "Log In")
+      return {
+        title
+      }
+    }
+  },
+}
+</script>
diff --git a/front/src/views/auth/Signup.vue b/front/src/views/auth/Signup.vue
new file mode 100644
index 0000000000000000000000000000000000000000..51849030a4f4dfb790333698a83e7bd568821c0a
--- /dev/null
+++ b/front/src/views/auth/Signup.vue
@@ -0,0 +1,36 @@
+<template>
+  <main class="main pusher" v-title="labels.title">
+    <section class="ui vertical stripe segment">
+      <div class="ui small text container">
+        <signup-form :default-invitation="defaultInvitation" :next="next">
+          <h2 slot="title"><translate translate-context="Content/Signup/Title">Create a funkwhale account</translate></h2>
+        </signup-form>
+      </div>
+    </section>
+  </main>
+</template>
+
+<script>
+import axios from "axios"
+import logger from "@/logging"
+
+import SignupForm from "@/components/auth/SignupForm"
+
+export default {
+  components: {
+    SignupForm
+  },
+  props: {
+    defaultInvitation: { type: String, required: false, default: null },
+    next: { type: String, default: "/" }
+  },
+  computed: {
+    labels() {
+      let title = this.$pgettext("*/Signup/Title", "Sign Up")
+      return {
+        title,
+      }
+    }
+  }
+}
+</script>