Modern web applications demand sophisticated frontend builds. Laravel Elixir simplifies Webpack configuration, providing a clean API for compiling JavaScript, Sass, and more. At ZIRA Software, we use Elixir to build complex single-page applications with hot module replacement and optimized production bundles.
Why Laravel Elixir?
Elixir advantages:
- Clean, fluent API
- Wraps Webpack complexity
- Automatic versioning
- BrowserSync integration
- Source maps
- Notifications
- Multiple environment configs
What Elixir does:
- Compiles ES6 to ES5
- Bundles JavaScript modules
- Compiles Sass/Less to CSS
- Minifies assets
- Versions files for cache busting
- Copies files
- Combines files
Installation
Laravel 5.3+ includes Elixir by default.
package.json:
{
"private": true,
"scripts": {
"dev": "gulp",
"watch": "gulp watch",
"production": "gulp --production"
},
"devDependencies": {
"gulp": "^3.9.1",
"laravel-elixir": "^6.0.0",
"laravel-elixir-webpack-official": "^1.0.2",
"bootstrap-sass": "^3.3.7",
"jquery": "^3.1.0"
}
}
Install:
npm install
Basic Gulpfile
gulpfile.js:
const elixir = require('laravel-elixir');
require('laravel-elixir-webpack-official');
elixir(mix => {
// Compile Sass
mix.sass('app.scss');
// Compile JavaScript with Webpack
mix.webpack('app.js');
});
Directory structure:
resources/
├── assets/
├── js/
│ └── app.js
└── sass/
└── app.scss
Run:
gulp watch # Watch for changes
gulp --production # Minified production build
JavaScript Compilation
ES6 Modules
resources/assets/js/app.js:
import Vue from 'vue';
import axios from 'axios';
window.Vue = Vue;
window.axios = axios;
// Configure axios
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
axios.defaults.headers.common['X-CSRF-TOKEN'] = document.querySelector('meta[name="csrf-token"]').content;
// Import components
import TaskList from './components/TaskList.vue';
Vue.component('task-list', TaskList);
// Boot Vue
const app = new Vue({
el: '#app',
data: {
message: 'Hello Laravel!'
},
mounted() {
console.log('Application started');
}
});
resources/assets/js/components/TaskList.vue:
<template>
<div class="task-list">
<h2>{{ title }}</h2>
<ul>
<li v-for="task in tasks" :key="task.id">
{{ task.name }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
title: 'My Tasks',
tasks: []
};
},
mounted() {
this.fetchTasks();
},
methods: {
fetchTasks() {
axios.get('/api/tasks')
.then(response => {
this.tasks = response.data;
});
}
}
};
</script>
<style scoped>
.task-list {
padding: 20px;
}
</style>
gulpfile.js:
elixir(mix => {
mix.webpack('app.js');
});
Multiple Entry Points
elixir(mix => {
mix.webpack([
'app.js',
'admin.js',
'dashboard.js'
]);
});
Custom Webpack Config
gulpfile.js:
const elixir = require('laravel-elixir');
elixir.config.js.webpack.config = {
module: {
loaders: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
}
};
elixir(mix => {
mix.webpack('app.js');
});
Sass Compilation
resources/assets/sass/app.scss:
// Variables
$primary-color: #3490dc;
$font-family: 'Nunito', sans-serif;
// Import Bootstrap
@import "node_modules/bootstrap-sass/assets/stylesheets/bootstrap";
// Custom styles
body {
font-family: $font-family;
background: #f5f5f5;
}
.btn-primary {
background: $primary-color;
border-color: darken($primary-color, 10%);
&:hover {
background: darken($primary-color, 10%);
}
}
.card {
background: white;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
padding: 20px;
}
gulpfile.js:
elixir(mix => {
mix.sass('app.scss');
});
Outputs to: public/css/app.css
Multiple Sass Files
elixir(mix => {
mix.sass([
'app.scss',
'admin.scss'
]);
});
Custom Output Directory
elixir(mix => {
mix.sass('app.scss', 'public/assets/css');
});
Versioning for Cache Busting
gulpfile.js:
elixir(mix => {
mix.sass('app.scss')
.webpack('app.js')
.version(['css/app.css', 'js/app.js']);
});
Generates:
public/css/app-abc123.csspublic/js/app-def456.jspublic/build/rev-manifest.json
Use in Blade:
<link rel="stylesheet" href="{{ elixir('css/app.css') }}">
<script src="{{ elixir('js/app.js') }}"></script>
Outputs:
<link rel="stylesheet" href="/css/app-abc123.css">
<script src="/js/app-def456.js"></script>
BrowserSync
Auto-reload browser on changes:
elixir(mix => {
mix.sass('app.scss')
.webpack('app.js')
.browserSync({
proxy: 'laravel.test', // Your local domain
files: [
'app/**/*.php',
'resources/views/**/*.php',
'public/js/**/*.js',
'public/css/**/*.css'
]
});
});
Start:
gulp watch
Browser opens at http://localhost:3000 and reloads automatically!
Copying Files
Copy fonts:
elixir(mix => {
mix.copy('node_modules/font-awesome/fonts', 'public/fonts');
});
Copy images:
elixir(mix => {
mix.copy('resources/assets/images', 'public/images');
});
Combining Files
elixir(mix => {
mix.scripts([
'jquery.js',
'bootstrap.js',
'app.js'
], 'public/js/all.js');
mix.styles([
'bootstrap.css',
'app.css'
], 'public/css/all.css');
});
Source Maps
Enabled by default in development:
gulp // Includes source maps
gulp --production // No source maps
Notifications
Desktop notifications on compile:
Already enabled! Shows success/failure notifications.
Environment-Specific Builds
gulpfile.js:
const elixir = require('laravel-elixir');
elixir(mix => {
if (elixir.config.production) {
// Production: minified, no source maps
mix.sass('app.scss')
.webpack('app.js')
.version(['css/app.css', 'js/app.js']);
} else {
// Development: source maps, BrowserSync
mix.sass('app.scss')
.webpack('app.js')
.browserSync({ proxy: 'laravel.test' });
}
});
Vue.js Single File Components
Install loader:
npm install vue-loader vue-template-compiler --save-dev
Component: resources/assets/js/components/UserCard.vue:
<template>
<div class="user-card">
<img :src="user.avatar" :alt="user.name">
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
<button @click="sendMessage" class="btn btn-primary">
Send Message
</button>
</div>
</template>
<script>
export default {
props: ['user'],
methods: {
sendMessage() {
this.$emit('message-sent', this.user);
}
}
};
</script>
<style scoped>
.user-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
text-align: center;
}
.user-card img {
width: 100px;
height: 100px;
border-radius: 50%;
}
.user-card h3 {
margin: 10px 0;
}
</style>
Use component:
import UserCard from './components/UserCard.vue';
Vue.component('user-card', UserCard);
In Blade:
<user-card :user="{{ json_encode($user) }}"></user-card>
React Support
Install:
npm install react react-dom babel-preset-react --save-dev
.babelrc:
{
"presets": ["es2015", "react"]
}
Component: resources/assets/js/components/Welcome.jsx:
import React from 'react';
class Welcome extends React.Component {
render() {
return (
<div className="welcome">
<h1>Hello, {this.props.name}!</h1>
</div>
);
}
}
export default Welcome;
app.js:
import React from 'react';
import ReactDOM from 'react-dom';
import Welcome from './components/Welcome.jsx';
ReactDOM.render(
<Welcome name="Laravel" />,
document.getElementById('root')
);
Rollup for Libraries
gulpfile.js:
const elixir = require('laravel-elixir');
require('laravel-elixir-rollup-official');
elixir(mix => {
mix.rollup('app.js');
});
Image Optimization
Install:
npm install laravel-elixir-imagemin --save-dev
gulpfile.js:
const elixir = require('laravel-elixir');
require('laravel-elixir-imagemin');
elixir(mix => {
mix.imagemin('images/**/*');
});
TypeScript Support
Install:
npm install typescript ts-loader --save-dev
tsconfig.json:
{
"compilerOptions": {
"target": "es5",
"module": "es2015",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false
},
"exclude": [
"node_modules"
]
}
webpack.config.js:
module.exports = {
module: {
loaders: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.ts', '.tsx', '.js']
}
};
Custom Elixir Extensions
Create extension:
const elixir = require('laravel-elixir');
elixir.extend('myTask', function(src, dest) {
new elixir.Task('myTask', function() {
// Your custom task logic
return gulp.src(src)
.pipe(/* your transformations */)
.pipe(gulp.dest(dest));
})
.watch(src);
});
Use extension:
elixir(mix => {
mix.myTask('src/**/*.js', 'dist');
});
Production Optimization
package.json scripts:
{
"scripts": {
"dev": "NODE_ENV=development gulp",
"watch": "NODE_ENV=development gulp watch",
"hot": "NODE_ENV=development gulp watch --hot",
"production": "NODE_ENV=production gulp --production"
}
}
Run production build:
npm run production
This will:
- Minify JavaScript
- Minify CSS
- Remove source maps
- Optimize images
- Version assets
Hot Module Replacement
For instant updates without page reload:
elixir(mix => {
mix.webpack('app.js', null, null, {
hot: true
});
});
Start:
gulp watch --hot
In your app:
if (module.hot) {
module.hot.accept();
}
Debugging Build Issues
Verbose output:
gulp --verbose
Check Webpack stats:
elixir.config.js.webpack.config.stats = 'verbose';
Performance Tips
- Use production builds - Significantly smaller
- Enable caching - Faster rebuilds
- Lazy load routes - Split code by route
- Tree shaking - Remove unused code
- Scope CSS - Use scoped styles in Vue
- Optimize images - Use imagemin
- CDN for vendor libs - Load jQuery/Vue from CDN
Migrating to Laravel Mix
Laravel 5.4+ uses Laravel Mix instead of Elixir.
Mix is simpler:
const mix = require('laravel-mix');
mix.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css')
.version();
Most Elixir code works with minimal changes.
Conclusion
Laravel Elixir brings modern frontend tooling to Laravel with minimal configuration. Webpack, Babel, Sass compilation, and hot reloading work out of the box. At ZIRA Software, Elixir powers our frontend builds, letting us focus on features instead of build config.
Start simple with Sass and JavaScript compilation. Add versioning, then BrowserSync. You'll wonder how you lived without it.
Building complex frontend applications with Laravel? Contact ZIRA Software to discuss modern asset pipelines, Vue.js integration, and performant frontend architectures.