skip to Main Content

Project setup

The project is laravel 10 setup with sail and thus using vite.
Also bootstrap 5 and bootstrap icons is added through npm.

What I want to achieve

I am using blade components and I want each component to have its own scss file for styling.
I want the styling to be only loaded when using the component.

What I want to know

Is it possible to use seperate scss files in my laravel project?
How do I tell laravel which scss file is for which blade file?

If its not possible to use scss files how can I achieve my goal with css files?

Where would I put the scss/css files in my project?
I read that when using vite you can put them in the resource folder and otherwise they should be in the public folder.

What I have now

If I am missing files that are necessary for an answer please inform me.
I want this question to be understandable and usable for other users so thats why I try to include as little as possible of my own files.

My vite config:

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import path from 'path';

export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/scss/app.scss', 'resources/js/app.js'],
            refresh: true,
        }),
    ],
    resolve: {
        alias: {
            '~bootstrap': path.resolve(__dirname, 'node_modules/bootstrap'),
            '~bootstrap-icons': path.resolve(__dirname, 'node_modules/bootstrap-icons'),
        }
    }
});

My package json:

{
    "private": true,
    "type": "module",
    "scripts": {
        "dev": "vite",
        "build": "vite build"
    },
    "devDependencies": {
        "axios": "^1.1.2",
        "laravel-vite-plugin": "^0.7.5",
        "sass": "^1.62.1",
        "vite": "^4.0.0"
    },
    "dependencies": {
        "@popperjs/core": "^2.11.7",
        "bootstrap": "^5.2.3",
        "bootstrap-icons": "^1.10.5"
    }
}

My folder structure:

public/
└── build
resources/
├── js/
│   ├── app.js
│   └── bootstrap.js
├── scss/
│   ├── components/
│   │   └── form/
│   │       └── text-input.scss
│   └── app.scss
└── views/
    ├── auth/
    │   └── login.blade.php
    ├── components/
    │   └── form/
    │       └── text-input.blade.php
    └── layouts/
        └── authentication.blade.php

What I have tried

I have tried to put the scss files in resources and public folder and reference them with @pushonce(‘mystack) in my blade view.
This is without success since the scss stays empty.

When adding my scss file to the app.js as import it is loaded but with a generic build name and loaded on every single page of the application, which im trying to avoid.

3

Answers


  1. Chosen as BEST ANSWER

    Answer

    I have found out that styling to be only loaded when using the component. Is not possible with Vite, but it is possible in laravel mix.

    Solution

    For the solution we will migrate to laravel mix and change a little of the setup.

    Migrate from vite to laravel mix

    Follow the steps from here: https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-vite-to-laravel-mix

    Npm packages

    These packages have to be installed as dev dependencies:

    • sass
    • sass-loader
    • glob
    • resolve-url-loader

    sail npm install sass sass-loader glob resolve-url-loader --save-dev
    We need these packages to compile the scss files and add them automaticly to laravel mix.

    If you also want the public directory to be cleaned each time when build add: npm install rimraf --save-dev.

    Update the webpackconfig

    Change the extension from webpack.mix.js to webpack.mix.cjs if using ES module.
    Add this inside the webpack.mix.cjs file:

    const mix = require('laravel-mix');
    const glob = require('glob');
    // Using rimraf for cleaning (optional)
    const rimraf = require('rimraf');
    
    /*
     |--------------------------------------------------------------------------
     | Mix Asset Management
     |--------------------------------------------------------------------------
     |
     | Mix provides a clean, fluent API for defining some Webpack build steps
     | for your Laravel applications. By default, we are compiling the CSS
     | file for the application as well as bundling up all the JS files.
     |
     */
    
    // Clean the "public/css" and "public/js" directories before each build (optional)
    mix.before(() => {
        rimraf.sync('public/css');
        rimraf.sync('public/js');
    });
    
    // Loads all javascript files in resources/js and compiles them to public/js with the same directory structure
    const jsFiles = glob.sync('resources/js/**/*.js');
    for (const file of jsFiles) {
        const outputFilePath = file.replace(/^resources/js/, 'public/js');
        mix.js(file, outputFilePath);
    }
    
    // Loads all scss files in resources/scss and compiles them to public/css with the same directory structure
    const scssFiles = glob.sync('resources/scss/**/*.scss');
    for (const file of scssFiles) {
        const outputFilePath = file.replace(/^resources/scss/, 'public/css').replace(/.scss$/, '.css');
        mix.sass(file, outputFilePath);
    }
    

    This will add javascript and scss files in your resources directory automaticly to laravel mix so its compiled to the public folder. It also keeps the same directory structure. When adding new files be sure to use npm run dev so they are compiled and watched by laravel mix.

    Usage

    You now can use @push('styles') or @pushonce('styles') inside your blade files to load the styles/js only when the blade view is used.

    Example

    Have in your root file (Layout):

    <head>
      @stack('styles')
    </head>
    <body>
      @stack('scripts')
    </body>
    

    Now in the blade view you're using:

    @pushOnce('scripts')
      <script src="{{ mix('js/myCustomFolder/myComponent.js') }}" defer></script>
    @endPushOnce
    @pushOnce('scripts')
      <link rel="stylesheet" href="{{ mix('css/myCustomFolder/myComponent.css') }}">
    @endPushOnce
    

    I use pushOnce since I sometimes reuse components multiple times on a page and only want the styling and javascript to be loaded once.

    Npm webpack

    Be sure to run webpack when using laravel with npm run dev or npm run watch. When adding new files you should rerun the command again.

    Pay attention for this

    For this you write the styling in your .scss files in the resource/scss/** directory but when referencing it in the blade view you use the public/css directory with the .css extension.


  2. You can use Laravel mix if you only want to use them inside Blade components and refer the scss to css converted file for each scss file in each Blade components

    https://laravel.com/docs/10.x/mix

    https://laravel-mix.com/

    Login or Signup to reply.
  3. What you’re trying to achieve is easy to do with @push and @stack directives. The following assumes an older "Template Inheritance" style, but I’ve tested it to work with component approach as well, as long as you use the @stack directive in the layout.

    In your layout:

    <!DOCTYPE html>
    <html>
    <head>
        @stack('styles')
    </head>
    
    <body>
        @yield('content')
    </body>
    </html>
    

    In your view file (auth/login.blade):

    @extends('authentication')
    
    @section('content')
        <form>
            @include('text-input', ['name' => 'username'])
    
            <button>Submit</button>
        </form>
    @endsection
    
    @push('styles')
        <link rel="stylesheet" href="your-vite-or-mix-call-here" />
    @endpush
    

    In your "component" file:

    <input type="text" class="text-input" name="{{$name}}" />
    
    @push('styles')
        <style>
            .text-input {
                background-color: beige;
            }
        </style>
    @endpush
    

    The result is the rendered markup that includes the styles from both: your view file and any "components" down the line.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search