diff --git a/package/gluon-setup-mode/Makefile b/package/gluon-setup-mode/Makefile
index d149e9186acffaf78e408b8c0a05d4b53f1d92ec..b76ab6c0220cb9bf79dcc02e70a614ba0e59edd9 100644
--- a/package/gluon-setup-mode/Makefile
+++ b/package/gluon-setup-mode/Makefile
@@ -15,7 +15,7 @@ define Package/gluon-setup-mode
   SECTION:=gluon
   CATEGORY:=Gluon
   TITLE:=Setup mode
-  DEPENDS:=+gluon-core +gluon-web +ubus +uhttpd +dnsmasq
+  DEPENDS:=+gluon-core +gluon-web-model +ubus +uhttpd +dnsmasq
 endef
 
 define Package/gluon-setup-mode/description
diff --git a/package/gluon-web-model/Makefile b/package/gluon-web-model/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..7139d9064e8ede989e8d33c5669d4678c5b1d0dd
--- /dev/null
+++ b/package/gluon-web-model/Makefile
@@ -0,0 +1,36 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-web-model
+PKG_VERSION:=1
+
+
+include ../gluon.mk
+
+PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
+
+
+define Package/gluon-web-model
+  SECTION:=gluon
+  CATEGORY:=Gluon
+  TITLE:=Minimal Lua web framework derived from LuCI (model support)
+  DEPENDS:=+gluon-web
+endef
+
+
+define Build/Prepare
+	mkdir -p $(PKG_BUILD_DIR)
+endef
+
+define Build/Compile
+	$(call GluonBuildI18N,gluon-web-model,i18n)
+	$(call GluonSrcDiet,./luasrc,$(PKG_BUILD_DIR)/luadest/)
+endef
+
+define Package/gluon-web-model/install
+	$(CP) ./files/* $(1)/
+	$(CP) $(PKG_BUILD_DIR)/luadest/* $(1)/
+	$(call GluonInstallI18N,gluon-web-model,$(1))
+
+endef
+
+$(eval $(call BuildPackage,gluon-web-model))
diff --git a/package/gluon-web/files/lib/gluon/web/view/model/dynlist.html b/package/gluon-web-model/files/lib/gluon/web/view/model/dynlist.html
similarity index 100%
rename from package/gluon-web/files/lib/gluon/web/view/model/dynlist.html
rename to package/gluon-web-model/files/lib/gluon/web/view/model/dynlist.html
diff --git a/package/gluon-web/files/lib/gluon/web/view/model/form.html b/package/gluon-web-model/files/lib/gluon/web/view/model/form.html
similarity index 100%
rename from package/gluon-web/files/lib/gluon/web/view/model/form.html
rename to package/gluon-web-model/files/lib/gluon/web/view/model/form.html
diff --git a/package/gluon-web/files/lib/gluon/web/view/model/fvalue.html b/package/gluon-web-model/files/lib/gluon/web/view/model/fvalue.html
similarity index 100%
rename from package/gluon-web/files/lib/gluon/web/view/model/fvalue.html
rename to package/gluon-web-model/files/lib/gluon/web/view/model/fvalue.html
diff --git a/package/gluon-web/files/lib/gluon/web/view/model/lvalue.html b/package/gluon-web-model/files/lib/gluon/web/view/model/lvalue.html
similarity index 100%
rename from package/gluon-web/files/lib/gluon/web/view/model/lvalue.html
rename to package/gluon-web-model/files/lib/gluon/web/view/model/lvalue.html
diff --git a/package/gluon-web/files/lib/gluon/web/view/model/section.html b/package/gluon-web-model/files/lib/gluon/web/view/model/section.html
similarity index 100%
rename from package/gluon-web/files/lib/gluon/web/view/model/section.html
rename to package/gluon-web-model/files/lib/gluon/web/view/model/section.html
diff --git a/package/gluon-web/files/lib/gluon/web/view/model/tvalue.html b/package/gluon-web-model/files/lib/gluon/web/view/model/tvalue.html
similarity index 100%
rename from package/gluon-web/files/lib/gluon/web/view/model/tvalue.html
rename to package/gluon-web-model/files/lib/gluon/web/view/model/tvalue.html
diff --git a/package/gluon-web/files/lib/gluon/web/view/model/value.html b/package/gluon-web-model/files/lib/gluon/web/view/model/value.html
similarity index 100%
rename from package/gluon-web/files/lib/gluon/web/view/model/value.html
rename to package/gluon-web-model/files/lib/gluon/web/view/model/value.html
diff --git a/package/gluon-web/files/lib/gluon/web/view/model/valuewrapper.html b/package/gluon-web-model/files/lib/gluon/web/view/model/valuewrapper.html
similarity index 100%
rename from package/gluon-web/files/lib/gluon/web/view/model/valuewrapper.html
rename to package/gluon-web-model/files/lib/gluon/web/view/model/valuewrapper.html
diff --git a/package/gluon-web/files/lib/gluon/web/view/model/wrapper.html b/package/gluon-web-model/files/lib/gluon/web/view/model/wrapper.html
similarity index 100%
rename from package/gluon-web/files/lib/gluon/web/view/model/wrapper.html
rename to package/gluon-web-model/files/lib/gluon/web/view/model/wrapper.html
diff --git a/package/gluon-web/files/lib/gluon/web/www/static/resources/gluon-web.js b/package/gluon-web-model/files/lib/gluon/web/www/static/resources/gluon-web.js
similarity index 100%
rename from package/gluon-web/files/lib/gluon/web/www/static/resources/gluon-web.js
rename to package/gluon-web-model/files/lib/gluon/web/www/static/resources/gluon-web.js
diff --git a/package/gluon-web-model/i18n/de.po b/package/gluon-web-model/i18n/de.po
new file mode 100644
index 0000000000000000000000000000000000000000..b0761f347cfd715752cefad4a354b152754db12e
--- /dev/null
+++ b/package/gluon-web-model/i18n/de.po
@@ -0,0 +1,23 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"PO-Revision-Date: 2013-03-29 12:13+0200\n"
+"Last-Translator: Matthias Schiffer <mschiffer@universe-factory.net>\n"
+"Language-Team: German\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgid "One or more fields contain invalid values!"
+msgstr "Ein oder mehrere Felder enthalten ungültige Werte!"
+
+msgid "One or more required fields have no value!"
+msgstr "Ein oder mehr benötigte Felder sind nicht ausgefüllt!"
+
+msgid "Reset"
+msgstr "Zurücksetzen"
+
+msgid "Save"
+msgstr "Speichern"
diff --git a/package/gluon-web-model/i18n/fr.po b/package/gluon-web-model/i18n/fr.po
new file mode 100644
index 0000000000000000000000000000000000000000..61c59d88ff1e53d2431050130af8f58573e4fab7
--- /dev/null
+++ b/package/gluon-web-model/i18n/fr.po
@@ -0,0 +1,23 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"PO-Revision-Date: 2013-12-22 17:11+0200\n"
+"Last-Translator: goofy <pierre.gaufillet@gmail.com>\n"
+"Language-Team: French\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+msgid "One or more fields contain invalid values!"
+msgstr "Un ou plusieurs champs contiennent des valeurs incorrectes !"
+
+msgid "One or more required fields have no value!"
+msgstr "Un ou plusieurs champs n'ont pas de valeur !"
+
+msgid "Reset"
+msgstr "Remise à zéro"
+
+msgid "Save"
+msgstr "Soumettre"
diff --git a/package/gluon-web-model/i18n/gluon-web-model.pot b/package/gluon-web-model/i18n/gluon-web-model.pot
new file mode 100644
index 0000000000000000000000000000000000000000..3fd79546fea74542d2ff0186ef0f71b410cf254e
--- /dev/null
+++ b/package/gluon-web-model/i18n/gluon-web-model.pot
@@ -0,0 +1,14 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+msgid "One or more fields contain invalid values!"
+msgstr ""
+
+msgid "One or more required fields have no value!"
+msgstr ""
+
+msgid "Reset"
+msgstr ""
+
+msgid "Save"
+msgstr ""
diff --git a/package/gluon-web/javascript/gluon-web.js b/package/gluon-web-model/javascript/gluon-web.js
similarity index 100%
rename from package/gluon-web/javascript/gluon-web.js
rename to package/gluon-web-model/javascript/gluon-web.js
diff --git a/package/gluon-web-model/luasrc/usr/lib/lua/gluon/web/model.lua b/package/gluon-web-model/luasrc/usr/lib/lua/gluon/web/model.lua
new file mode 100644
index 0000000000000000000000000000000000000000..4914dee6dd8c8bf2ece5a45811d2dc7b6c42cddd
--- /dev/null
+++ b/package/gluon-web-model/luasrc/usr/lib/lua/gluon/web/model.lua
@@ -0,0 +1,68 @@
+-- Copyright 2008 Steven Barth <steven@midlink.org>
+-- Copyright 2017-2018 Matthias Schiffer <mschiffer@universe-factory.net>
+-- Licensed to the public under the Apache License 2.0.
+
+module('gluon.web.model', package.seeall)
+
+local fs = require 'nixio.fs'
+local classes = require 'gluon.web.model.classes'
+
+local util = require 'gluon.web.util'
+local instanceof = util.instanceof
+
+-- Loads a model from given file, creating an environment and returns it
+local function load(filename, i18n)
+	local func = assert(loadfile(filename))
+
+	setfenv(func, setmetatable({}, {__index =
+		function(tbl, key)
+			return classes[key] or i18n[key] or _G[key]
+		end
+	}))
+
+	local models = { func() }
+
+	for k, model in ipairs(models) do
+		if not instanceof(model, classes.Node) then
+			error("model definition returned an invalid model object")
+		end
+		model.index = k
+	end
+
+	return models
+end
+
+return function(config, http, renderer, name, pkg)
+	local hidenav = false
+
+	local modeldir = config.base_path .. '/model/'
+	local filename = modeldir..name..'.lua'
+
+	if not fs.access(filename) then
+		error("Model '" .. name .. "' not found!")
+	end
+
+	local i18n = setmetatable({
+		i18n = renderer.i18n
+	}, {
+		__index = renderer.i18n(pkg)
+	})
+
+	local maps = load(filename, i18n)
+
+	for _, map in ipairs(maps) do
+		map:parse(http)
+	end
+	for _, map in ipairs(maps) do
+		map:handle()
+		hidenav = hidenav or map.hidenav
+	end
+
+	renderer.render('layout', {
+		content = 'model/wrapper',
+		env = {
+			maps = maps,
+		},
+		hidenav = hidenav,
+	})
+end
diff --git a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/model.lua b/package/gluon-web-model/luasrc/usr/lib/lua/gluon/web/model/classes.lua
similarity index 89%
rename from package/gluon-web/luasrc/usr/lib/lua/gluon/web/model.lua
rename to package/gluon-web-model/luasrc/usr/lib/lua/gluon/web/model/classes.lua
index 00f77fe357507ee735a9a8e3869f2be769b9f2ea..ac7696929f6fe7850149db7f8548a101c8185d9b 100644
--- a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/model.lua
+++ b/package/gluon-web-model/luasrc/usr/lib/lua/gluon/web/model/classes.lua
@@ -1,12 +1,11 @@
 -- Copyright 2008 Steven Barth <steven@midlink.org>
--- Copyright 2017 Matthias Schiffer <mschiffer@universe-factory.net>
+-- Copyright 2017-2018 Matthias Schiffer <mschiffer@universe-factory.net>
 -- Licensed to the public under the Apache License 2.0.
 
-module("gluon.web.model", package.seeall)
+module("gluon.web.model.classes", package.seeall)
 
 local util = require "gluon.web.util"
 
-local fs         = require "nixio.fs"
 local datatypes  = require "gluon.web.model.datatypes"
 local class      = util.class
 local instanceof = util.instanceof
@@ -15,41 +14,6 @@ FORM_NODATA  =  0
 FORM_VALID   =  1
 FORM_INVALID = -1
 
--- Loads a model from given file, creating an environment and returns it
-function load(config, name, renderer, pkg)
-	local modeldir = config.base_path .. "/model/"
-
-	if not fs.access(modeldir..name..".lua") then
-		error("Model '" .. name .. "' not found!")
-	end
-
-	local func = assert(loadfile(modeldir..name..".lua"))
-
-	local i18n = setmetatable({
-		i18n = renderer.i18n
-	}, {
-		__index = renderer.i18n(pkg)
-	})
-
-
-	setfenv(func, setmetatable({}, {__index =
-		function(tbl, key)
-			return _M[key] or i18n[key] or _G[key]
-		end
-	}))
-
-	local models = { func() }
-
-	for k, model in ipairs(models) do
-		if not instanceof(model, Node) then
-			error("model definition returned an invalid model object")
-		end
-		model.index = k
-	end
-
-	return models
-end
-
 
 local function parse_datatype(code)
 	local match, arg, arg2
@@ -86,7 +50,7 @@ function Node:__init__(title, description, name)
 	self.name = name
 	self.index = nil
 	self.parent = nil
-	self.package = 'gluon-web'
+	self.package = 'gluon-web-model'
 end
 
 function Node:append(obj)
diff --git a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/model/datatypes.lua b/package/gluon-web-model/luasrc/usr/lib/lua/gluon/web/model/datatypes.lua
similarity index 100%
rename from package/gluon-web/luasrc/usr/lib/lua/gluon/web/model/datatypes.lua
rename to package/gluon-web-model/luasrc/usr/lib/lua/gluon/web/model/datatypes.lua
diff --git a/package/gluon-web-theme/Makefile b/package/gluon-web-theme/Makefile
index db86de9ac9736fb9542fc2e71b8dc0a006d21f67..87e97bbf0b65c01ba0935c7d621e11d87e1e3b71 100644
--- a/package/gluon-web-theme/Makefile
+++ b/package/gluon-web-theme/Makefile
@@ -16,7 +16,7 @@ define Package/gluon-web-theme
   SECTION:=gluon
   CATEGORY:=Gluon
   TITLE:=gluon-web theme
-  DEPENDS:=+gluon-core +gluon-web
+  DEPENDS:=+gluon-core +gluon-web-model
 endef
 
 define Build/Prepare
diff --git a/package/gluon-web-theme/files/lib/gluon/web/view/themes/gluon/layout.html b/package/gluon-web-theme/files/lib/gluon/web/view/themes/gluon/layout.html
index febe91b525524b75305466b5742d6f2733961844..fb24b3a3f94fb61ae21360579e9574ea6aac82fd 100644
--- a/package/gluon-web-theme/files/lib/gluon/web/view/themes/gluon/layout.html
+++ b/package/gluon-web-theme/files/lib/gluon/web/view/themes/gluon/layout.html
@@ -142,7 +142,7 @@ You may obtain a copy of the License at
 			<%
 				ok, err = pcall(renderer.render, content, env, pkg)
 				if not ok then
-					renderer.render('error500', {message = err}, 'gluon-web')
+					renderer.render('error/500', {message = err}, 'gluon-web')
 				end
 			%>
 
diff --git a/package/gluon-web/Makefile b/package/gluon-web/Makefile
index eecd0ac0b5b757f41681c173d0d8b55bfdc81af9..b66e3c24e25c8c1c3a212a074b6abab27b911ee7 100644
--- a/package/gluon-web/Makefile
+++ b/package/gluon-web/Makefile
@@ -3,7 +3,6 @@ include $(TOPDIR)/rules.mk
 PKG_NAME:=gluon-web
 PKG_VERSION:=1
 
-PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
 
 include ../gluon.mk
 
diff --git a/package/gluon-web/files/lib/gluon/web/view/error404.html b/package/gluon-web/files/lib/gluon/web/view/error/404.html
similarity index 100%
rename from package/gluon-web/files/lib/gluon/web/view/error404.html
rename to package/gluon-web/files/lib/gluon/web/view/error/404.html
diff --git a/package/gluon-web/files/lib/gluon/web/view/error500.html b/package/gluon-web/files/lib/gluon/web/view/error/500.html
similarity index 100%
rename from package/gluon-web/files/lib/gluon/web/view/error500.html
rename to package/gluon-web/files/lib/gluon/web/view/error/500.html
diff --git a/package/gluon-web/i18n/de.po b/package/gluon-web/i18n/de.po
index ed1dc7fc281d53311e00974146ed00aaa44929c9..9481f60f6bceb8dd5ff3d8516cda4cc215ff3a10 100644
--- a/package/gluon-web/i18n/de.po
+++ b/package/gluon-web/i18n/de.po
@@ -16,18 +16,6 @@ msgstr "Interner Serverfehler"
 msgid "Not Found"
 msgstr "Nicht Gefunden"
 
-msgid "One or more fields contain invalid values!"
-msgstr "Ein oder mehrere Felder enthalten ungültige Werte!"
-
-msgid "One or more required fields have no value!"
-msgstr "Ein oder mehr benötigte Felder sind nicht ausgefüllt!"
-
-msgid "Reset"
-msgstr "Zurücksetzen"
-
-msgid "Save"
-msgstr "Speichern"
-
 msgid "Sorry, the object you requested was not found."
 msgstr "Entschuldigung, das anfgeforderte Objekt wurde nicht gefunden."
 
diff --git a/package/gluon-web/i18n/fr.po b/package/gluon-web/i18n/fr.po
index 759e98d75cfd2ca185f79372dff10b44b003accb..631a80cbfd9369cd6ce999b89781add6dc33c11a 100644
--- a/package/gluon-web/i18n/fr.po
+++ b/package/gluon-web/i18n/fr.po
@@ -16,18 +16,6 @@ msgstr "Erreur Serveur Interne"
 msgid "Not Found"
 msgstr "Pas trouvé"
 
-msgid "One or more fields contain invalid values!"
-msgstr "Un ou plusieurs champs contiennent des valeurs incorrectes !"
-
-msgid "One or more required fields have no value!"
-msgstr "Un ou plusieurs champs n'ont pas de valeur !"
-
-msgid "Reset"
-msgstr "Remise à zéro"
-
-msgid "Save"
-msgstr "Soumettre"
-
 msgid "Sorry, the object you requested was not found."
 msgstr "Désolé, l'objet que vous avez demandé n'as pas été trouvé."
 
diff --git a/package/gluon-web/i18n/gluon-web.pot b/package/gluon-web/i18n/gluon-web.pot
index 3d387b2e36ab55589ad62925f94a1209fa976c22..d3b60ac0bcbb793e6ce8084400fa53d40035b576 100644
--- a/package/gluon-web/i18n/gluon-web.pot
+++ b/package/gluon-web/i18n/gluon-web.pot
@@ -7,18 +7,6 @@ msgstr ""
 msgid "Not Found"
 msgstr ""
 
-msgid "One or more fields contain invalid values!"
-msgstr ""
-
-msgid "One or more required fields have no value!"
-msgstr ""
-
-msgid "Reset"
-msgstr ""
-
-msgid "Save"
-msgstr ""
-
 msgid "Sorry, the object you requested was not found."
 msgstr ""
 
diff --git a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/dispatcher.lua b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/dispatcher.lua
index 0879f76b4e878a2f7aaec2f7036eb07c3dd59b8f..4acc2c6c943b9cc175b1792bfc24407c604f63b4 100644
--- a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/dispatcher.lua
+++ b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/dispatcher.lua
@@ -146,26 +146,7 @@ local function dispatch(config, http, request)
 				model = function(name)
 					local pkg = _pkg
 					return function()
-						local hidenav = false
-
-						local model = require "gluon.web.model"
-						local maps = model.load(config, name, renderer, pkg)
-
-						for _, map in ipairs(maps) do
-							map:parse(http)
-						end
-						for _, map in ipairs(maps) do
-							map:handle()
-							hidenav = hidenav or map.hidenav
-						end
-
-						renderer.render("layout", {
-							content = "model/wrapper",
-							env = {
-								maps = maps,
-							},
-							hidenav = hidenav,
-						})
+						require('gluon.web.model')(config, http, renderer, name, pkg)
 					end
 				end,
 
@@ -198,7 +179,7 @@ local function dispatch(config, http, request)
 	if not node or not node.target then
 		http:status(404, "Not Found")
 		renderer.render("layout", {
-			content = "error404",
+			content = "error/404",
 			env = {
 				message =
 					"No page is registered at '/" .. table.concat(request, "/") .. "'.\n" ..
@@ -215,7 +196,7 @@ local function dispatch(config, http, request)
 	if not ok then
 		http:status(500, "Internal Server Error")
 		renderer.render("layout", {
-			content = "error500",
+			content = "error/500",
 			env = {
 				message =
 					"Failed to execute dispatcher target for entry '/" .. table.concat(request, "/") .. "'.\n" ..