# Assets
Krayin uses Vite (opens new window) to bundle JavaScript and CSS for the admin. Each package ships its own Vite config and registers itself with the central krayin-vite integration so that hot-module-reload, builds, and asset URLs all work the same way as the core packages.
This page walks through wiring a package called Example end to end โ from where the source files live, to compiling them with Vite, to injecting them into the admin layout.
For Laravel-side context see the Laravel Vite docs (opens new window).
# ๐ Where assets live
Source files live under src/Resources/assets/ inside your package. Standard sub-folders:
packages
โโโ Webkul
โโโ Example
โโโ src
โโโ ...
โโโ Resources
โโโ assets
โโโ css
โ โโโ app.css
โโโ js
โ โโโ app.js
โโโ images
โโโ fonts
| Folder | Holds |
|---|---|
css/ | Stylesheets (Tailwind input + custom CSS) |
js/ | JavaScript entry point and any modules |
images/ | Static image assets |
fonts/ | Web fonts |
To make Vite track images and fonts (so it copies them into the build output), add this single line to your js/app.js:
// Track all images and fonts for publishing.
import.meta.glob(['../images/**', '../fonts/**']);
# โ๏ธ Register the package with krayin-vite
Krayin discovers your package's assets through a config file you merge into krayin-vite.viters.
# 1. Create Config/krayin-vite.php
<?php
return [
'example' => [
'hot_file' => 'example-vite.hot',
'build_directory' => 'example/build',
'package_assets_directory' => 'src/Resources/assets',
],
];
| Key | Purpose |
|---|---|
hot_file | Filename Vite writes to public/ while the dev server is running. Must be unique per package. |
build_directory | Where compiled assets land under public/. |
package_assets_directory | Path inside your package to the source files. |
# 2. Merge it in your service provider
In register():
$this->mergeConfigFrom(
dirname(__DIR__) . '/Config/krayin-vite.php',
'krayin-vite.viters',
);
# 3. Publish the build output
In boot():
$this->publishes([
__DIR__ . '/../../publishable/build' => public_path('example/build'),
], 'public');
# ๐ ๏ธ Configure Vite for your package
You need five config files at the root of your package (alongside src/), not inside src/:
packages
โโโ Webkul
โโโ Example
โโโ composer.json
โโโ package.json
โโโ postcss.config.js
โโโ tailwind.config.js
โโโ vite.config.js
โโโ src
โโโ ...
# composer.json
{
"name": "krayin/example",
"license": "MIT",
"authors": [
{ "name": "krayin", "email": "[email protected]" }
],
"require": {},
"autoload": {
"psr-4": {
"Webkul\\Example\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"Webkul\\Example\\Providers\\ExampleServiceProvider"
]
}
},
"minimum-stability": "dev"
}
# package.json
{
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"devDependencies": {
"autoprefixer": "^10.4.16",
"axios": "^1.7.4",
"laravel-vite-plugin": "^1.0",
"postcss": "^8.4.23",
"tailwindcss": "^3.3.2",
"vite": "^5.4.12",
"vue": "^3.4.21"
},
"dependencies": {
"@vee-validate/i18n": "^4.9.1",
"@vee-validate/rules": "^4.9.1",
"@vitejs/plugin-vue": "^4.2.3"
}
}
| Script | What it does |
|---|---|
npm run dev | Starts the Vite dev server with hot-module-reload. |
npm run build | Produces optimised assets for production. |
# postcss.config.js
module.exports = ({ env }) => ({
plugins: [require('tailwindcss')(), require('autoprefixer')()],
});
Two plugins:
- Tailwind CSS โ processes your utility classes.
- Autoprefixer โ adds vendor prefixes for older browsers.
# tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/Resources/**/*.blade.php', './src/Resources/**/*.js'],
theme: {
container: {
center: true,
screens: { '4xl': '1920px' },
padding: { DEFAULT: '16px' },
},
screens: {
sm: '525px',
md: '768px',
lg: '1024px',
xl: '1240px',
'2xl': '1440px',
'3xl': '1680px',
'4xl': '1920px',
},
extend: {
colors: {},
fontFamily: {
inter: ['Inter'],
icon: ['icomoon'],
},
},
},
darkMode: 'class',
plugins: [],
safelist: [{ pattern: /icon-/ }],
};
| Key | Purpose |
|---|---|
content | Files Tailwind scans for class names. Must include your Blade and JS. |
theme.screens | Custom breakpoints matching the rest of Krayin. |
darkMode: 'class' | Toggles dark mode via a CSS class, not a media query. |
safelist | Classes Tailwind should always emit, even if it can't see them in source (icon-* classes are added dynamically by the menu config). |
# vite.config.js
import { defineConfig, loadEnv } from 'vite';
import vue from '@vitejs/plugin-vue';
import laravel from 'laravel-vite-plugin';
import path from 'path';
export default defineConfig(({ mode }) => {
const envDir = '../../../';
Object.assign(process.env, loadEnv(mode, envDir));
return {
build: { emptyOutDir: true },
envDir,
server: {
host: process.env.VITE_HOST || 'localhost',
port: process.env.VITE_PORT || 5173,
cors: true,
},
plugins: [
vue(),
laravel({
hotFile: '../../../public/example-vite.hot',
publicDirectory: 'publishable',
buildDirectory: 'build',
input: [
'src/Resources/assets/css/app.css',
'src/Resources/assets/js/app.js',
],
refresh: true,
}),
],
experimental: {
renderBuiltUrl(filename, { hostType }) {
if (hostType === 'css') {
return path.basename(filename);
}
},
},
};
});
Match the `hotFile` name everywhere
The hotFile in vite.config.js and the hot_file value in krayin-vite.php must match exactly (just .hot vs without โ the config strips the extension). A mismatch silently breaks hot reload.
# ๐ Inject the assets into the admin layout
# 1. Create a Blade partial
packages/Webkul/Example/src/Resources/views/example-style.blade.php:
{{ vite()->set(['src/Resources/assets/css/app.css', 'src/Resources/assets/js/app.js'], 'example') }}
The vite() helper picks the right URL automatically โ dev-server URL while npm run dev is running, hashed build URL otherwise.
# 2. Register it via an event listener
Inside packages/Webkul/Example/src/Providers/EventServiceProvider.php:
use Illuminate\Support\Facades\Event;
public function boot(): void
{
Event::listen('admin.layout.head.before', function ($viewRenderEventManager) {
$viewRenderEventManager->addTemplate(
'example::example-style'
);
});
}
Krayin's admin layout fires admin.layout.head.before as it renders โ your listener inserts the partial inside <head>.
# โจ๏ธ Build commands
| Command | When to use it |
|---|---|
npm run dev | While developing โ gives you hot reload as you edit CSS / JS / Vue files. |
npm run build | Before deploying. Produces minified assets in publishable/build/. |
After a production build, run the publish step so the assets are copied into public/:
php artisan vendor:publish --tag=public --force
# ๐งช Verify
- Start the dev server:
npm run devinside the package directory. - Reload the admin and open DevTools โ Network โ your CSS/JS should be served from
localhost:5173. - Edit
app.css, save, watch the admin re-style without a manual reload. If nothing reloads, check thehotFilename in both krayin-vite.php and vite.config.js match.
For a production check: npm run build && php artisan vendor:publish --tag=public --force, then reload the admin โ assets should be served from public/example/build/ with hashed filenames.
# ๐ Next steps
- Blade Components โ use Krayin's prebuilt admin components so you don't have to ship as much CSS yourself.
- Admin Menu โ refer back to add a sidebar icon for your package once the assets are loaded.
โ Admin Menu Blade Components โ