---
url: >-
  https://docs.corbitaltech.dev/products/whatsmark-saas/customization/custom-module/getting-started.md
description: >-
  Professional guide for creating custom modules in WhatsMarkSaaS with Laravel
  12, Livewire 3, and multi-tenant architecture
---

# Getting Started - Custom Module Development

::: warning Minimum Version Requirement
This feature requires **WhatsMarkSaaS v2.0.0 or higher**. Ensure your installation meets this requirement before proceeding.
:::

## Prerequisites {#prerequisites-custom-module}

You must have working knowledge of the following technologies before proceeding:

* **Laravel 12** - Service providers, routing, migrations, Eloquent ORM
* **Livewire 3** - Component lifecycle, properties, actions
* **Tailwind CSS v3** - Utility-first CSS framework
* **Alpine.js v3** - Frontend interactivity
* **Multi-tenant architecture** - Tenant-aware data operations and isolation patterns

## Overview

Custom modules extend WhatsMarkSaaS functionality without modifying core code. Each module is a self-contained package that can include:

* Routes (web and API endpoints)
* Controllers and middleware
* Eloquent models with tenant scoping
* Blade templates and Livewire components
* Translations with multi-tenant support
* Configuration files
* Database migrations and seeders
* Event hooks (WordPress-style actions and filters)
* Frontend assets (JavaScript, CSS compiled via Vite)

Modules reside in the `Modules/` directory at the project root and can be activated, deactivated, and removed independently.

## Step 1: Create a Custom Module

Execute the following Artisan command to generate module scaffolding:

```bash
php artisan module:make OrderTracker
```

This command generates a complete module structure at `Modules/OrderTracker/`.

**Command options:**

| Option | Description |
|--------|-------------|
| `--force` | Overwrites existing module files |
| (none) | Creates new module (fails if exists) |

## Step 2: Review Generated Structure

The scaffolding command generates the following directory structure:

```
Modules/OrderTracker/
 Config/
    config.php
 Console/
 Database/
    Migrations/
    Seeders/
    Factories/
 Http/
    Controllers/
       OrderTrackerController.php
    Middleware/
 Livewire/
 Models/
 Providers/
    OrderTrackerServiceProvider.php
    RouteServiceProvider.php
 resources/
    assets/
       js/
       css/
    lang/
       en.json
       tenant_en.json
    views/
        index.blade.php
 Routes/
    web.php
    api.php
 OrderTracker.php
 module.json
 composer.json
 package.json
 vite.config.js
 README.md
```

## Step 3: Understand the Service Provider

The generated `OrderTrackerServiceProvider.php` handles module registration:

### boot() Method

The `boot()` method registers module resources:

```php
public function boot(): void
{
    // Register module translations
    $this->loadTranslationsFrom(module_path($this->moduleName, 'resources/lang'), 'ordertracker');

    // Publish module configuration
    $this->publishes([
        module_path($this->moduleName, 'Config/config.php') => config_path('ordertracker.php'),
    ], 'ordertracker-config');

    // Register module views
    $this->loadViewsFrom(__DIR__.'/../resources/views', 'ordertracker');

    // Load module migrations
    $this->loadMigrationsFrom(module_path($this->moduleName, 'Database/Migrations'));
}
```

### register() Method

The `register()` method registers the route service provider:

```php
public function register(): void
{
    $this->app->register(RouteServiceProvider::class);
}
```

### Registering Livewire Components

Add Livewire component registration in the `boot()` method:

```php
use Livewire\Livewire;

public function boot(): void
{
    // ... existing registrations ...

    // Register Livewire components
    Livewire::component('order-tracker::dashboard', \Modules\OrderTracker\Livewire\Dashboard::class);
}
```

## Step 4: Configure the Main Module Class

The `OrderTracker.php` class extends `Corbital\ModuleManager\Support\Module` and provides lifecycle methods:

```php
<?php

namespace Modules\OrderTracker;

use Corbital\ModuleManager\Support\Module;

class OrderTracker extends Module
{
    /**
     * Register WordPress-style action and filter hooks.
     */
    public function registerHooks(): void
    {
        // Register hooks here
    }

    /**
     * Execute operations during module activation.
     * Runs before migrations and seeders.
     */
    public function activate(): void
    {
        // Setup tasks
    }

    /**
     * Execute operations during module deactivation.
     */
    public function deactivate(): void
    {
        // Cleanup tasks
    }

    /**
     * Execute operations after activation completes.
     * Runs after migrations and seeders.
     */
    public function activated(): void
    {
        // Post-activation tasks
    }

    /**
     * Execute operations after deactivation completes.
     */
    public function deactivated(): void
    {
        // Post-deactivation tasks
    }
}
```

### Lifecycle Method Execution Order

**Activation sequence:**

1. `activate()` - Pre-activation setup
2. Run migrations (if `auto_migrations` enabled)
3. Run seeders (if `auto_migrations.seed` enabled)
4. Register routes, views, translations
5. `activated()` - Post-activation tasks

**Deactivation sequence:**

1. `deactivate()` - Pre-deactivation cleanup
2. Unregister routes
3. `deactivated()` - Post-deactivation tasks

## Step 5: Activate the Module

Execute the activation command:

```bash
php artisan module:activate OrderTracker
```

**What happens during activation:**

1. Migrations run automatically (if `auto_migrations` enabled in config)
2. Seeders execute after migrations complete
3. Routes become available and registered with Laravel
4. Views and translations are registered
5. The `activated()` lifecycle method is called

**Configuration for auto-migrations:**

Check `config/modules.php`:

```php
'auto_migrations' => [
    'enabled' => true,
    'seed' => true,
],
```

## Step 6: Access the Module

After activation, the module routes are accessible via browser:

```
https://your-domain.com/order-tracker
```

The route prefix is automatically generated as kebab-case from the module name.

**Route examples:**

| Module Name | Route Prefix |
|-------------|--------------|
| OrderTracker | /order-tracker |
| ProductCatalog | /product-catalog |
| CustomerSupport | /customer-support |

## Step 7: Deactivate (When Needed)

To disable the module without deleting files:

```bash
php artisan module:deactivate OrderTracker
```

**What happens during deactivation:**

1. Routes are unregistered
2. The `deactivate()` lifecycle method is called
3. Module files remain on disk
4. Database tables and data are preserved

## Understanding module.json

The `module.json` file defines module metadata and dependencies:

```json
{
    "name": "OrderTracker",
    "alias": "order-tracker",
    "namespace": "Modules\\OrderTracker\\",
    "provider": "Modules\\OrderTracker\\Providers\\OrderTrackerServiceProvider",
    "author": "Module Author",
    "url": "",
    "version": "1.0.0",
    "description": "The OrderTracker Module",
    "keywords": [],
    "order": 0,
    "providers": [
        "Modules\\OrderTracker\\Providers\\OrderTrackerServiceProvider"
    ],
    "aliases": [],
    "require": [],
    "requires_at": "2.0.0",
    "conflicts": [],
    "type": "custom"
}
```

### Field Reference

| Field | Type | Description |
|-------|------|-------------|
| `name` | string | Module name in PascalCase |
| `alias` | string | URL-friendly identifier (kebab-case) |
| `namespace` | string | PSR-4 namespace root |
| `provider` | string | Main service provider class (FQCN) |
| `version` | string | Semantic version (e.g., `1.0.0`) |
| `order` | integer | Load order (lower = earlier, default: 0) |
| `require` | array | Module dependencies (other module names) |
| `requires_at` | string | Minimum WhatsMarkSaaS version required |
| `conflicts` | array | Modules that conflict with this module |
| `type` | string | Always `custom` for developer modules |

***

## View and Translation Loading

### View Namespace Convention

Views are loaded with lowercase module name as namespace:

```php
return view('ordertracker::index', ['data' => $data]);
```

**Loading priority (first match wins):**

1. `resources/views/modules/ordertracker/` - Override location (theme customization)
2. `Modules/OrderTracker/resources/views/` - Default module views

### Translation Files

**Standard translations** - `resources/lang/en.json`:

```json
{
    "Order Tracker": "Order Tracker",
    "View Orders": "View Orders"
}
```

**Tenant-specific translations** - `resources/lang/tenant_en.json`:

```json
{
    "welcome_message": "Welcome to Order Tracker"
}
```

**Usage in views:**

```blade
{{ t('Order Tracker') }}
```

**Loading priority (first match wins):**

1. `resources/lang/modules/ordertracker/` - Override location
2. `Modules/OrderTracker/resources/lang/` - Default module translations

## Next Steps {#next-steps-custom-module-getting-started}

You have successfully created and activated a custom module. Proceed with:

1. **[Module Structure Reference](./module-structure.md)** - Understand file organization and conventions
2. **[Routing & Controllers](./routing-controllers.md)** - Define web and API routes
3. **[Views & Translations](./views-translations.md)** - Create Blade templates and multi-language support
4. **[Hooks & Events](./hooks-events.md)** - Integrate with WhatsMarkSaaS hook system
5. **[Helper Functions](./helper-functions.md)** - Use module utilities and helpers

## Troubleshooting

### Module routes not showing

1. Verify activation status: `php artisan module:activate ModuleName`
2. Clear route cache: `php artisan route:clear`
3. Check route list: `php artisan route:list --path=module-name`

### Views not found

1. Verify view files exist in `Modules/ModuleName/resources/views/`
2. Use correct namespace: `modulename::viewfile` (lowercase, no hyphens)
3. Clear view cache: `php artisan view:clear`

### Translations not loading

1. Check JSON files exist in `Modules/ModuleName/resources/lang/`
2. Ensure valid JSON format (no trailing commas)
3. Clear translation cache: `php artisan cache:clear`

### Migration errors on activation

1. Check migration files for syntax errors
2. Verify `tenant_id` column is included where needed
3. Run manually: `php artisan migrate --path=Modules/ModuleName/Database/Migrations`

### Module not autoloading

1. Verify `composer.json` has correct namespace: `Modules\\ModuleName\\`
2. Run: `composer dump-autoload`
3. Check `module.json` has correct `provider` path

***

*This guide is for WhatsMarkSaaS v2.0.0+ custom module development.*
