Technologies
- Next.js 16.3 (App Router + Turbopack)
- React 19.2
- TypeScript 5.9
- Bootstrap 5.3 + SCSS
- Redux Toolkit 2.11
System Requirements
- Node.js version >= 18.18 (LTS 20+ recommended)
- npm version >= 9 (ships with Node.js)
- Yarn package (Optional)
- Visual Studio Code
- Terminal
- A modern browser — Chrome, Edge, Firefox or Safari
– Built with Next.js 16 App Router and React 19
– TypeScript throughout
– File-system based routing with route groups
– Per-page SEO metadata through generateMetadata
– Static export (output: "export") — deploys to any static host
– 390+ ready-made pages grouped by module
– Role based dashboards — Admin, Teacher, Student and Parent
– Academic, HRM, Finance, Attendance, Library, Transport, Hostel, Communication
and Settings modules
– Turbopack powered dev server and production build
– Redux Toolkit store with theme settings persisted to localStorage
– Service layer that switches between mock data and a real REST API
– Light / Dark theme and four layout modes — default, mini, boxed and RTL
– Path aliases for clean imports
– Fully responsive design
– Cross browser compatible
– Clean and well commented code
– Easy to customize
– Developer friendly
– And many more...
Project Overview
The app/ tree handles routing and metadata only —
the actual page UI lives in views/. Template internals sit in
core/ and data access in services/.
preskool/
│
└── nextjs/
│
├── public/
│ ├── assets/ # images, fonts and static files
│ └── favicon.png
│
├── src/
│ ├── app/ # App Router — routing and metadata only
│ │ ├── (pages)/ # main layout: header, sidebar, theme
│ │ │ ├── admin-dashboard/
│ │ │ │ ├── page.tsx
│ │ │ │ └── adminDashboardClient.tsx
│ │ │ └── … # 390+ page folders
│ │ ├── (authentication)/ # bare auth layout: login, errors…
│ │ ├── providers/ # AppProviders, AuthProvider
│ │ ├── globals.scss
│ │ ├── layout.tsx # root layout: global CSS + providers
│ │ ├── page.tsx # "/" entry
│ │ ├── rootRedirectClient.tsx # client-side "/" → "/login" hop
│ │ ├── not-found.tsx
│ │ └── notFoundClient.tsx
│ │
│ ├── components/
│ │ ├── bootstrap-js/ # loads Bootstrap's JS on the client
│ │ ├── routing/ # ProtectedRoute, RoleGuard
│ │ ├── ui/
│ │ └── workspace/
│ │
│ ├── config/ # env.ts (typed env), metadata.ts
│ ├── constants/
│ │
│ ├── core/
│ │ ├── common/ # header, sidebar, modals, theme-settings
│ │ ├── data/ # redux store, slices and menu JSON
│ │ └── modals/
│ │
│ ├── features/ # feature hooks — auth, dashboard, students
│ ├── hooks/ # useForm, useQuery, useToasts, …
│ ├── mocks/ # static fixtures returned by mock services
│ ├── routes/
│ │ └── all_routes.tsx # single source of route paths
│ ├── services/ # api/, auth/, dashboard/, students/
│ ├── style/
│ │ ├── css/
│ │ ├── fonts/
│ │ ├── icon/
│ │ └── scss/
│ ├── types/
│ ├── utils/
│ ├── views/ # every page's UI, grouped by module
│ └── environment.tsx # base_path and img_path
│
├── eslint.config.mjs
├── next.config.ts
├── package.json
├── package-lock.json
└── tsconfig.json
Structure Overview
PreSkool Next.js uses the App Router with two
route groups. (pages) renders inside the main layout — header,
sidebar and theme settings — while (authentication) renders the
bare login and error screens. Route groups are wrapped in parentheses, so
they organise the folder tree without appearing in the URL.
The app is configured with output: "export", which
produces a fully static site. There is no server runtime — no route handlers, no
middleware and no server actions.
Configuration — next.config.ts:
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "export",
basePath: "/nextjs",
};
export default nextConfig;
Root Layout — src/app/layout.tsx:
import { Metadata } from "next";
import "../../node_modules/bootstrap/dist/css/bootstrap.min.css";
import BootstrapJs from "@/components/bootstrap-js/bootstrapjs";
import "./globals.scss";
import AppProviders from "./providers/AppProviders";
export const metadata: Metadata = {
title: "Preskool Admin Template",
icons: { icon: "favicon.png" },
};
export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="en">
<body>
<AppProviders>{children}</AppProviders>
<BootstrapJs />
</body>
</html>
);
}
Route Groups
| Group | What it renders |
|---|---|
src/app/(pages)/ |
Every application page, wrapped in the main layout — header, sidebar, theme settings panel |
src/app/(authentication)/ |
Login, register, forgot password, lock screen, error and maintenance pages in a bare layout |
src/app/providers/ |
Redux store and auth context composition |
Note
Images are rendered through
<ImageWithBasePath>
(src/core/common/imageWithBasePath). It prefixes
img_path from src/environment.tsx so assets resolve
correctly under the basePath in a static export.
Scripts Overview
Both the dev server and the production build run on
Turbopack. Because the project is configured for static
export, npm run build already writes the exported site — there
is no separate export step.
You can find the scripts in the package.json file. Here
are the available commands:
| Command | Description |
|---|---|
npm run dev |
Starts the development server with Turbopack on
http://localhost:3000/nextjs |
npm run build |
Builds the application and static-exports every page to
out/ |
npm run start |
Serves a previous build |
npm run lint |
Runs ESLint using the flat config in
eslint.config.mjs |
Package.json Scripts Section:
"scripts": {
"dev": "next dev --turbopack",
"build": "next build --turbopack",
"start": "next start",
"lint": "eslint"
}
After unzipping the template files, you will find our product Preskool -> nextjs (Source code)
Node.js (18.18 or above) and npm are required to run the Next.js project
Open a terminal in the nextjs folder and install the
dependencies:
npm install (or) npm install --legacy-peer-deps
To run the project in development mode, run the command:
npm run dev
The application starts on
http://localhost:3000/nextjs. The /nextjs
prefix comes from the basePath option in
next.config.ts — change it there if you deploy to a
different path.
If you want to make a build in production mode, run the following command in the root directory, otherwise the project will continue to run in the development mode.
npm run build
To install any plugins in your application, you have to run the following command
npm install plugin_name --save (or) npm install plugin_name --legacy-peer-deps
Deployment Note
Because output: "export" is set, the build
writes a fully static site to out/. Upload that folder to
any static host under the /nextjs path. Note that
redirects() has no effect in a static export — the
/ → /login hop is done client-side by
src/app/rootRedirectClient.tsx.
The Three File Pattern
Every page in the template follows the same shape — a server
page.tsx, a client wrapper and the view itself. Copy it exactly
when you add a page of your own.
1. src/app/(pages)/my-page/page.tsx — server
component, metadata only:
import { getPageMetadata } from "@/config/metadata";
import MyPageClient from "./myPageClient";
// This runs on the server
export const generateMetadata = () => {
return getPageMetadata("My Page");
};
export default function MyPagePage() {
return <MyPageClient />;
}
2. src/app/(pages)/my-page/myPageClient.tsx — the
client boundary:
"use client";
import dynamic from "next/dynamic";
const MyPageComponent = dynamic(() => import("@views/module/my-page"), {
ssr: false,
loading: () => <p></p>,
});
export default function MyPageClient() {
return <MyPageComponent />;
}
3. src/views/module/my-page/index.tsx — the UI
itself.
Why the split : a file marked "use client" cannot
export generateMetadata, and
dynamic(..., { ssr: false }) is illegal in a server component.
Keeping page.tsx on the server buys per-page SEO titles; the
Client file exists only to hold the directive and the dynamic
import. Never put "use client" in a page.tsx.
4. Register the path in
src/routes/all_routes.tsx:
export const all_routes = {
// …existing paths
myPage: "/my-page",
};
5. Link to it — never hard-code a URL string:
import Link from "next/link";
import { all_routes } from "@/routes/all_routes";
<Link href={all_routes.myPage}>My Page</Link>
Declared Once
Aliases are declared in compilerOptions.paths of
tsconfig.json. Next.js reads that file and resolves the same
aliases for the bundler, so there is nothing else to keep in sync.
| Alias | Target |
|---|---|
@/* | src/* |
@app/* | src/app/* |
@views/* | src/views/* |
@components/* | src/components/* |
@core/* | src/core/* |
@layouts/* | src/layouts/* |
@features/* | src/features/* |
@mocks/* | src/mocks/* |
@hooks, @hooks/* | src/hooks/* |
@services, @services/* | src/services/* |
@config, @config/* | src/config/* |
@constants, @constants/* | src/constants/* |
@utils, @utils/* | src/utils/* |
@routes, @routes/* | src/routes/* |
Usage:
import { getPageMetadata } from "@/config/metadata";
import { all_routes } from "@/routes/all_routes";
import ProtectedRoute from "@components/routing/ProtectedRoute";
import { studentsService } from "@services/students";
Runs Without a Backend
The template ships with mock data enabled and authentication
disabled, so it runs correctly with no .env.local file and no
server.
Each domain folder in src/services/ follows the same
four-file shape:
| File | Responsibility |
|---|---|
<domain>.types.ts |
TypeScript types shared by both implementations |
<domain>.mock.ts |
Resolves data from the fixtures in src/mocks/ |
<domain>.http.ts |
Real REST calls through the shared client |
<domain>.service.ts |
Picks mock or HTTP via createService |
createService
(src/services/api/create-service.ts) chooses the implementation from
env.useMockApi, so call sites never change. All HTTP traffic goes
through the single client in src/services/api/client.ts.
Connecting a Real Backend
Create a .env.local file in the nextjs
folder:
NEXT_PUBLIC_API_BASE_URL=https://api.example.com/v1
NEXT_PUBLIC_USE_MOCK_API=false
NEXT_PUBLIC_AUTH_ENABLED=true
| Variable | Default | Description |
|---|---|---|
NEXT_PUBLIC_APP_ENV |
development |
Environment label — development, staging or production |
NEXT_PUBLIC_API_BASE_URL |
empty | Base URL of the REST API |
NEXT_PUBLIC_USE_MOCK_API |
true |
Resolve data from static mocks instead of HTTP |
NEXT_PUBLIC_AUTH_ENABLED |
false |
Enforce authentication on protected routes |
src/config/env.ts is the only module that reads
process.env. While NEXT_PUBLIC_AUTH_ENABLED is
false, ProtectedRoute and RoleGuard render
their children unconditionally, so every route stays reachable. Flip it to
true once a real auth API is connected and the same components start
enforcing the session.
Important Note : Every NEXT_PUBLIC_* value is
inlined into the client bundle and is therefore public. Never put a secret or
a private key in one.
Theme state lives in Redux
(src/core/data/redux/themeSettingSlice.tsx) and is persisted to
localStorage. src/app/(pages)/layout.tsx mirrors that
state onto the <html> element as data attributes, and the
SCSS keys off them.
| Attribute | Values |
|---|---|
data-theme |
light, dark |
data-layout |
default, mini, boxed,
rtl |
data-sidebar |
light, dark, primary,
darkblack, darkblue |
data-sidebarbg |
default, sidebarbg1 …
sidebarbg6 |
data-color |
primary, violet, pink,
orange, green, red |
data-topbar |
white, dark, primary,
grey |
All of these are switched at runtime from the gear panel in the
header, which is implemented in
src/core/common/theme-settings. Because the values are written to
localStorage, the choice survives a page reload.
Open the Theme Settings panel from the gear icon in the
header and choose Dark under Mode. The selection is stored
in localStorage, so it is remembered on the next visit.
To make dark mode the default for every new visitor instead,
change the initial value in
src/core/data/redux/themeSettingSlice.tsx:
const initialState = {
// …
dataTheme: localStorage.getItem("dataTheme") || "dark_data_theme",
// …
};
src/app/(pages)/layout.tsx maps
dark_data_theme to data-theme="dark" on the
<html> element, which is what the SCSS reads.
Open the Theme Settings panel and pick the
RTL layout. src/app/(pages)/layout.tsx then
sets data-layout="rtl" on the <html> element
and the template stylesheet flips the layout direction.
If you want the Bootstrap grid and utilities themselves mirrored
as well, swap the Bootstrap stylesheet import in
src/app/layout.tsx for the RTL build:
// Replace this line
import "../../node_modules/bootstrap/dist/css/bootstrap.min.css";
// with this one
import "../../node_modules/bootstrap/dist/css/bootstrap.rtl.min.css";
RTL-specific overrides used by the template live under the
.layout-mode-rtl selector in
src/app/globals.scss.
The logo files live in public/assets/img/. The simplest
route is to replace those files with your own, keeping the same names and
roughly the same dimensions.
| File | Where it is used |
|---|---|
assets/img/logo.svg |
Full logo, expanded sidebar in light mode |
assets/img/logo-small.svg |
Compact logo, mini sidebar layout |
assets/img/logo-dark.svg |
Full logo, dark mode |
To point at differently named files instead, edit
src/core/common/header/index.tsx:
<Link href={routes.adminDashboard} className="logo logo-normal">
<ImageWithBasePath src="assets/img/logo.svg" alt="Logo" />
</Link>
<Link href={routes.adminDashboard} className="logo-small">
<ImageWithBasePath src="assets/img/logo-small.svg" alt="Logo" />
</Link>
<Link href={routes.adminDashboard} className="dark-logo">
<ImageWithBasePath src="assets/img/logo-dark.svg" alt="Logo" />
</Link>
The browser tab icon is public/favicon.png, declared in
the metadata.icons export of src/app/layout.tsx.
The template uses Inter. Changing it takes two edits.
1. Swap the import in
src/style/scss/components/_font.scss:
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
2. Update the family stack in
src/style/scss/utils/_variables.scss:
// Font Family
$font-family-primary: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
The base size and weight scale are declared in the same file
($font-size-base, $font-weight-medium and friends),
so you can adjust typography globally from one place.
Six preset accent colors ship with the template and can be switched at runtime from the Theme Settings panel — primary, violet, pink, orange, green and red.
To change the brand color itself, edit the theme variables in
src/style/scss/utils/_variables.scss:
// Theme Colors Variables
$primary: #3D5EE1;
$secondary: #6FCCD8;
$success: #1ABE17;
$info: #0F65CD;
$warning: #EAB300;
$danger: #E82646;
$dark: #202C4B;
$light: #E9EDF4;
Hover shades are derived from these with
color.adjust, so changing $primary updates
$primary-hover automatically. The dev server recompiles the SCSS as
soon as you save.
Need Support?
If this documentation does not address your questions, please feel free to contact us via email at support@dreamstechnologies.com
Reach the team at GMT+5:30. Typical reply within 12–24 hours on weekdays — rarely up to 48 hrs during holidays. Support is available to verified buyers for template-related issues.
Contact SupportImportant Note : We strive to offer top-notch support, but it's only available to verified buyers and for template-related issues such as bugs and errors. Custom changes and third-party module setups are not covered.
Do you need a customized application for your business?
If you need a customized application for your business depends on your specific requirements and goals, Please contact us. Customization can be the key to success, ensuring your project perfectly aligns with your unique goals and requirements.
Don't Miss Out on the Benefits of Customization!
Unlock the potential of your project. It's time to ensure your project isn't another cookie-cutter solution but truly unique and effective one.
Discover how customization can make a difference in your project's success. Let's create a solution that's as unique as your vision!
We'll tailor the application to meet your specific needs and preferences.
We will upload your website to the server and ensure it is live.
Thank You
Thank you once again for downloading Preskool.
We hope you're enjoying your experience, and we kindly request that you take a
moment to share your valuable review and rating with us.
