Vue3 Admin Template Without UI‑Framework Dependency – Features, Layout, Routing, State Management, and SVG Icon Builder
This article presents a lightweight, UI‑framework‑free Vue3 admin template, detailing its visual style configuration, core layout, permission‑based routing, TypeScript‑based state handling, custom AJAX request wrapper, and an SVG icon bundler, along with code examples and package information.
Overview
This document introduces a lightweight, UI‑framework‑free admin template built with vue.js (Vue 3) that provides visual style configuration, a dynamic layout system, permission‑based routing, TypeScript‑friendly state management, custom AJAX request handling, and an SVG icon builder.
Key Features
Visual configuration of CSS through a dedicated css variable, allowing style changes without editing code.
Switchable layout styles (top bar + multi‑level side menu) with responsive design and custom scrollbar support.
Only essential dependencies: vue, vue-router, and a minimal set of utilities.
High compatibility and extensibility – any third‑party UI library can be added without modifying core components.
Package.json is deliberately minimal, containing only necessary dependencies and devDependencies.
Package Configuration
{
"dependencies": {
"nprogress": "0.2.0",
"vue": "3.2.45",
"vue-router": "4.1.6"
},
"devDependencies": {
"@types/node": "~18.15.11",
"@types/nprogress": "0.2.0",
"@vitejs/plugin-vue": "4.1.0",
"@vitejs/plugin-vue-jsx": "3.0.1",
"sass": "~1.61.0",
"typescript": "5.0.4",
"vite": "4.2.1",
"vue-tsc": "1.2.0"
}
}Core Layout
The layout combines a top bar and a collapsible side menu that can be completely hidden by moving it off‑screen, maximizing the content area. Visual style can be edited through the built‑in CSS configurator, eliminating the need to read CSS files.
Routing and Permission Control
Routing inherits the standard vue-router structure. Permissions are defined in the meta.auth array, while meta.name serves as a unique key for route caching.
import { RouteRecordRaw } from "vue-router";
export interface RouteMeta {
/** Sidebar menu name, document.title */
title: string;
/** External link, takes precedence over path */
link?: string;
/** SVG icon name */
icon?: string;
/** Hide this route from the sidebar */
hidden?: boolean;
/** Enable keep‑alive caching */
keepAlive?: boolean;
/** Array of user type IDs that can access the route */
auth?: Array<number>;
}
/** Custom route type extending RouteRecordRaw */
export type RouteItem = {
name?: string;
children?: Array<RouteItem>;
meta: RouteMeta;
} & RouteRecordRaw;State Management
With Vue 3, the project avoids Vuex and instead uses TypeScript Readonly declarations for state objects, providing a simpler and more type‑safe approach.
Network Requests
A lightweight native ajax wrapper is used. The request function returns a promise that resolves to an object where res.code === 1 indicates success.
export interface TableItem {
id: number;
type: "load" | "update";
time: string;
}
/**
* @param params
*/
export function getData(params: PageInfo) {
return request<ApiResultList<TableItem>>("GET", "/getList", params);
}Example component usage:
<script lang="ts" setup>
import { ref } from "vue";
import { type TableItem, getData } from "api.ts";
const tableData = ref<TableItem>([]);
async function getTableData() {
const res = await getData({ pageSize: 10, currentPage: 1 });
if (res.code === 1) {
tableData.value = res.data.list; // .list is of type TableItem
// do some ...
}
}
</script>SVG Icon Builder
SVG files from a designated directory are transformed into <symbol> elements and injected into the page body, enabling easy use of icons via <svg><use>.
import { readFileSync, readdirSync } from "fs";
let idPerfix = "";
const svgTitle = /<svg([^>+].*?)>/;
const clearHeightWidth = /(width|height)="([^>+].*?)"/g;
const hasViewBox = /(viewBox="[^"]+"))/g;
const clearReturn = /(\r)|(
)/g;
function findSvgFile(dir: string): Array<string> {
const svgRes: Array<string> = [];
const dirents = readdirSync(dir, { withFileTypes: true });
dirents.forEach(function (dirent) {
if (dirent.isDirectory()) {
svgRes.push(...findSvgFile(dir + dirent.name + "/"));
} else {
const svg = readFileSync(dir + dirent.name)
.toString()
.replace(clearReturn, "")
.replace(svgTitle, function (_, group) {
let width = 0;
let height = 0;
let content = group.replace(clearHeightWidth, function (val1, val2, val3) {
if (val2 === "width") width = val3;
else if (val2 === "height") height = val3;
return "";
});
if (!hasViewBox.test(group)) {
content += `viewBox="0 0 ${width} ${height}"`;
}
return `<symbol id="${idPerfix}-${dirent.name.replace(".svg", "")}" ${content}>`;
})
.replace("</svg>", "</symbol>");
svgRes.push(svg);
}
});
return svgRes;
}
export function svgBuilder(path: string, perfix = "icon") {
if (path.trim() === "") return;
idPerfix = perfix;
const res = findSvgFile(path);
return {
name: "svg-transform",
transformIndexHtml(html: string) {
return html.replace("<body>", `<body>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
${res.join("")}
</svg>`);
}
};
}Conclusion
The template offers a minimal‑dependency, highly customizable admin UI for Vue 3 projects, covering visual styling, routing, state, data fetching, and SVG icon management, making it suitable for developers who prefer to build their own UI components rather than rely on third‑party UI libraries.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
