> ## Documentation Index
> Fetch the complete documentation index at: https://filamentphp.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Upgrading from v1.x

export const EditOnGitHub = ({version, path}) => {
  const url = `https://github.com/filamentphp/filament/edit/${version}/${path}`;
  return <div className="not-prose mt-16">
      <a href={url} target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-2 text-sm text-gray-500 transition hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" className="h-4 w-4">
          <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" />
        </svg>
        Edit this page on GitHub
      </a>
    </div>;
};

export const Footer = () => {
  const sponsorsByTier = JSON.parse(`{
  "agency_partner": [
    {
      "name": "Kirschbaum",
      "url": "https://kirschbaumdevelopment.com/solutions/filament-development",
      "filename": "kirschbaum.svg"
    }
  ],
  "gold": [
    {
      "name": "Agiledrop",
      "url": "https://www.agiledrop.com/laravel?utm_source=filament",
      "filename": "agiledrop.svg"
    },
    {
      "name": "Baiz.ai",
      "url": "https://baiz.ai",
      "filename": "baiz-ai.svg"
    },
    {
      "name": "CMS Max",
      "url": "https://cmsmax.com?ref=filamentphp.com",
      "filename": "cms-max.svg"
    },
    {
      "name": "Mailtrap",
      "url": "https://mailtrap.io/email-sending?utm_source=community&utm_medium=referral&utm_campaign=filament",
      "filename": "mailtrap.svg"
    },
    {
      "name": "SerpApi",
      "url": "https://serpapi.com/?utm_source=filamentphp",
      "filename": "serpapi.svg"
    }
  ]
}`);
  function shuffleArray(items) {
    const result = [...items];
    for (let index = result.length - 1; index > 0; index--) {
      const randomIndex = Math.floor(Math.random() * (index + 1));
      [result[index], result[randomIndex]] = [result[randomIndex], result[index]];
    }
    return result;
  }
  const sponsors = Object.entries(sponsorsByTier).flatMap(([, sponsors]) => shuffleArray(sponsors));
  return <div className="mt-16 flex flex-col gap-4">
      <h2 className="text-center text-2xl font-medium text-gray-800 dark:text-gray-200">
        Sponsored by
      </h2>

      <div className="not-prose flex flex-wrap items-center justify-center gap-5">
        {sponsors.map(sponsor => <a key={sponsor.name} className="footer-sponsor-card" href={sponsor.url} target="_blank" title={sponsor.name}>
            <img src={`/docs/images/sponsors/footer/${sponsor.filename}`} alt={sponsor.name} noZoom />
            <span className="line-pattern-overlay line-pattern-80" />
          </a>)}

        <a href="https://github.com/sponsors/danharrin" target="_blank" className="footer-sponsor-cta">
          <span className="sponsor-cta-content">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M5 12h14" />
              <path d="M12 5v14" />
            </svg>
            <span>Your logo here</span>
          </span>
          <span className="line-pattern-overlay line-pattern-60" />
        </a>
      </div>
    </div>;
};

<Warning>
  You are currently viewing the documentation for Filament 2.x, which is a previous version of Filament.

  Looking for the current stable version? Visit the [5.x documentation](/docs/5.x).
</Warning>

> If you see anything missing from this guide, please do not hesitate to [make a pull request](https://github.com/filamentphp/filament/edit/2.x/packages/admin/docs/09-upgrade-guide.md) to our repository! Any help is appreciated!

## High impact changes

### Property and method changes to resource and page classes

<details>
  <summary>
    Changes to Resource classes
  </summary>

  The `Filament\Resources\Forms\Form` class has been renamed to `Filament\Resources\Form`.

  The `Filament\Resources\Tables\Table` class has been renamed to `Filament\Resources\Table`.

  The following properties and method signatures been updated:

  ```php theme={"theme":"gruvbox-dark-hard"}
  protected static ?string $label; // Protected the property. Added the `?string` type.

  protected static ?string $model; // Protected the property. Added the `?string` type.

  protected static ?string $navigationIcon; // Renamed from `$icon`. Protected the property. Added the `?string` type.

  protected static ?string $navigationLabel; // Protected the property. Added the `?string` type.

  protected static ?int $navigationSort; // Protected the property. Added the `?int` type.

  protected static ?string $recordTitleAttribute; // Renamed from `$primaryColumn`. Protected the property. Added the `?string` type.

  protected static ?string $slug; // Protected the property. Added the `?string` type.

  public static function form(Form $form): Form; // Added the `Form` return type.

  public static function table(Table $table): Table; // Added the `Table` return type.

  public static function getRelations(): array; // Renamed from `relations()`. Added the `array` return type.

  public static function getPages(): array; // Renamed from `routes()`. Added the `array` return type.
  ```

  The syntax for registering pages in `getPages()` (formerly `routes()`) has been updated:

  ```php theme={"theme":"gruvbox-dark-hard"}
  public static function getPages(): array
  {
      return [
          'index' => Pages\ListUsers::route('/'),
          'create' => Pages\CreateUser::route('/create'),
          'edit' => Pages\EditUser::route('/{record}/edit'),
      ];
  }
  ```
</details>

<details>
  <summary>
    Changes to List page classes
  </summary>

  The following properties and method signatures been updated:

  ```php theme={"theme":"gruvbox-dark-hard"}
  protected static string $resource; // Protected the property. Added the `string` type.
  ```
</details>

<details>
  <summary>
    Changes to Create page classes
  </summary>

  The following properties and method signatures been updated:

  ```php theme={"theme":"gruvbox-dark-hard"}
  protected static string $resource; // Protected the property. Added the `string` type.
  ```
</details>

<details>
  <summary>
    Changes to Edit page classes
  </summary>

  The following properties and method signatures been updated:

  ```php theme={"theme":"gruvbox-dark-hard"}
  protected static string $resource; // Protected the property. Added the `string` type.
  ```
</details>

### Forms

The entire `Filament\Resources\Forms` namespace has been moved to `Filament\Forms`.

The `when()`, `only()` and `except()` methods have been removed. You may now pass a closure to any field configuration method, for example `hidden()`, which determines if the method should be applied. For more information, please see the [advanced forms documentation](../forms/advanced#using-closure-customization);

#### Fields

The `dependable()` method has been renamed to `reactive()`, to better describe its effects.

The `helpMessage()` method has been renamed to `helperText()`.

##### Checkbox

The `stacked()` method has been removed, and replaced with `inline(false)`.

##### Select

The `emptyOptionsMessage()` method has been renamed to `searchPrompt()`.

##### Tags input

The tags input component now writes to a JSON array by default. To continue using the old behavior, use `separator(',')` method.

##### Toggle

The `stacked()` method has been removed, and replaced with `inline(false)`.

#### Layout components

##### Fieldset

The form components within the fieldset now need to be in their own `schema()` method, instead of being passed into `make()`.

##### Grid

The form components within the grid now need to be in their own `schema()` method, instead of being passed into `make()`.

##### Section

The form components within the section now need to be in their own `schema()` method, instead of being passed into `make()`.

##### Tabs

The `Filament\Resources\Forms\Tab` component has been moved to `Filament\Forms\Tabs\Tab`.

The form components within each tab now need to be in their own `schema()` method, instead of being passed into `make()`.

### Tables

The entire `Filament\Resources\Tables` namespace has been moved to `Filament\Tables`.

The `only()` and `except()` methods have been removed. You may now pass a closure to any column or filter configuration method, which determines if the method should be applied.

#### Columns

Column class names now have `Column` at the end, for example `TextColumn` not `Text`.

The `currency()` method has been renamed to `money()`.

The `formatUsing()` method has been renamed to `formatStateUsing()`. It now accepts a `$state` parameter, instead of `$value`.

The `getValueUsing()` method has been renamed to `getStateUsing()`.

The `primary()` method has been removed from columns. All columns link to the record page by default unless another URL or action is specified for that column.

#### Filters

The filter class has been moved from `Filament\Resources\Tables\Filter` to `Filament\Tables\Filters\Filter`.

Filters now have a dedicated `query()` method for applying the query, instead of using the second parameter of the `make()` method. For more information, check out the [table builder filters documentation](../tables/filters).

The `apply()` method of reusable filters must now have the following signature:

```php theme={"theme":"gruvbox-dark-hard"}
public function apply(Builder $query, array $data = []): Builder
{
    // ...
}
```

### Published configuration updates

If you've published the v1.x `filament.php` configuration file, you should republish it:

```bash theme={"theme":"gruvbox-dark-hard"}
php artisan vendor:publish --tag=filament-config --force
```

If you had customized the `path`, `domain` or `default_filesystem_disk`, you should update the new file with these changes. If you're using `.env` variables for these settings, you won't need to make any changes when upgrading, and you may even choose to delete `filament.php`.

### Users

Filament v2.x does not include a dedicated `filament_users` table as it did in v1.x. By default, all `App\Models\User`s can access the admin panel locally, and in production you must apply the `FilamentUser` interface to the model to control admin access. You can read more about this [here](./users).

<details>
  <summary>
    <strong>Recommended:</strong> Are you using the <code>filament\_users</code> table, but would like to switch to <code>App\Models\User</code>?
  </summary>

  First, you'll need to copy the old migrations to your app, to ensure that Laravel does not complain about them missing:

  <details>
    <summary>
      <code>database/migrations/0000\_00\_00\_000000\_create\_filament\_users\_table.php</code>
    </summary>

    ```php theme={"theme":"gruvbox-dark-hard"}
    <?php

    use Illuminate\Database\Migrations\Migration;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Support\Facades\Schema;

    class CreateFilamentUsersTable extends Migration
    {
        public function up(): void
        {
            Schema::create('filament_users', function (Blueprint $table): void {
                $table->id();
                $table->string('avatar')->nullable();
                $table->string('email')->unique();
                $table->string('name');
                $table->string('password');
                $table->rememberToken();
                $table->timestamps();
            });
        }

        public function down(): void
        {
            Schema::dropIfExists('filament_users');
        }
    }
    ```
  </details>

  <details>
    <summary>
      <code>database/migrations/0000\_00\_00\_000001\_create\_filament\_password\_resets\_table.php</code>
    </summary>

    ```php theme={"theme":"gruvbox-dark-hard"}
    <?php

    use Illuminate\Database\Migrations\Migration;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Support\Facades\Schema;

    class CreateFilamentPasswordResetsTable extends Migration
    {
        public function up(): void
        {
            Schema::create('filament_password_resets', function (Blueprint $table): void {
                $table->string('email')->index();
                $table->string('token');
                $table->timestamp('created_at')->nullable();
            });
        }

        public function down(): void
        {
            Schema::dropIfExists('filament_password_resets');
        }
    }
    ```
  </details>

  Create a migration to drop the `filament_users` and `filament_password_resets` tables:

  ```php theme={"theme":"gruvbox-dark-hard"}
  <?php

  use Illuminate\Database\Migrations\Migration;
  use Illuminate\Support\Facades\Schema;

  class DropFilamentUsersAndFilamentPasswordResetsTables extends Migration
  {
      public function up(): void
      {
          Schema::dropIfExists('filament_users');
          Schema::dropIfExists('filament_password_resets');
      }
  }
  ```
</details>

<details>
  <summary>
    Are you using the <code>filament\_users</code> table, and would like to continue using it?
  </summary>

  To keep the `filament_users` and `filament_password_resets` tables in your app, you'll need to copy the old migrations and model into your app.

  <details>
    <summary>
      <code>database/migrations/0000\_00\_00\_000000\_create\_filament\_users\_table.php</code>
    </summary>

    ```php theme={"theme":"gruvbox-dark-hard"}
    <?php

    use Illuminate\Database\Migrations\Migration;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Support\Facades\Schema;

    class CreateFilamentUsersTable extends Migration
    {
        public function up(): void
        {
            Schema::create('filament_users', function (Blueprint $table): void {
                $table->id();
                $table->string('avatar')->nullable();
                $table->string('email')->unique();
                $table->string('name');
                $table->string('password');
                $table->rememberToken();
                $table->timestamps();
            });
        }

        public function down(): void
        {
            Schema::dropIfExists('filament_users');
        }
    }
    ```
  </details>

  <details>
    <summary>
      <code>database/migrations/0000\_00\_00\_000001\_create\_filament\_password\_resets\_table.php</code>
    </summary>

    ```php theme={"theme":"gruvbox-dark-hard"}
    <?php

    use Illuminate\Database\Migrations\Migration;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Support\Facades\Schema;

    class CreateFilamentPasswordResetsTable extends Migration
    {
        public function up(): void
        {
            Schema::create('filament_password_resets', function (Blueprint $table): void {
                $table->string('email')->index();
                $table->string('token');
                $table->timestamp('created_at')->nullable();
            });
        }

        public function down(): void
        {
            Schema::dropIfExists('filament_password_resets');
        }
    }
    ```
  </details>

  <details>
    <summary>
      <code>app/Models/FilamentUser.php</code>
    </summary>

    ```php theme={"theme":"gruvbox-dark-hard"}
    <?php

    namespace App\Models;

    use Filament\Models\Contracts;
    use Illuminate\Foundation\Auth\User as Authenticatable;

    class FilamentUser extends Authenticatable implements Contracts\FilamentUser, Contracts\HasAvatar
    {
        protected $guarded = [];

        protected $hidden = [
            'password',
            'remember_token',
        ];
        
        public function canAccessFilament(): bool
        {
            return true;
        }
        
        public function getFilamentAvatarUrl(): ?string
        {
            return $this->avatar;
        }
    }
    ```
  </details>
</details>

<details>
  <summary>
    Are you already using <code>App\Models\User</code>?
  </summary>

  1. Remove the `IsFilamentUser` trait from the model.
  2. Remove the `$filamentUserColumn` property if you use them. Instead, control admin access with `canAccessFilament()`.
  3. If you have a `canAccessFilament()` method, add a `bool` return type to it.
  4. Remove the `$filamentAdminColumn` and `$filamentRolesColumn` properties, and `isFilamentAdmin()` method, if you use them. Filament now only uses policies for authorization, so you may implement whichever roles system you wish there. We recommend [`spatie/laravel-permission`](https://github.com/spatie/laravel-permission).
</details>

## Medium impact changes

### Relation managers

`HasMany` relation manager classes should now extend `Filament\Resources\RelationManagers\HasManyRelationManager`. `MorphMany` relation manager classes should now extend `Filament\Resources\RelationManagers\MorphManyRelationManager`. `BelongsToMany` relation manager classes should now extend `Filament\Resources\RelationManagers\BelongsToManyRelationManager`.

The `Filament\Resources\Forms\Form` class has been renamed to `Filament\Resources\Form`.

The `Filament\Resources\Tables\Table` class has been renamed to `Filament\Resources\Table`.

The following properties and method signatures been updated:

```php theme={"theme":"gruvbox-dark-hard"}
protected static ?string $inverseRelationship; // Protected the property. Added the `?string` type.

protected static ?string $recordTitleAttribute; // Renamed from `$primaryColumn`. Protected the property. Added the `?string` type.

protected static string $relationship; // Protected the property. Added the `string` type.
```

### `Filament\Filament` facade renamed to `Filament\Facades\Filament`

You should be able to safely rename all instances of this class to the new one.

### Roles

Filament now only uses policies for authorization, so you may implement whichever roles system you wish there. We recommend [`spatie/laravel-permission`](https://github.com/spatie/laravel-permission).

You may remove any roles from the `App\Filament\Roles` directory, and delete any `authorization()` methods on your resources.

## Low impact changes

### `Filament::ignoreMigrations()` method removed

Since Filament doesn't have any migrations anymore, you don't need to ignore them.

### Property changes to custom page classes

The following properties and method signatures been updated:

```php theme={"theme":"gruvbox-dark-hard"}
protected static ?string $title; // Protected the property. Added the `?string` type.

protected static ?string $navigationLabel; // Protected the property. Added the `?string` type.

protected static ?string $slug; // Protected the property. Added the `?string` type.
```

### Theming changes

The theming system has entirely changed, to add support for Tailwind JIT's opacity features, which don't support static color codes.

Follow the instructions on the [appearance page](./appearance#building-themes) to find out how to compile your own Filament stylesheet.

<EditOnGitHub version="2.x" path="packages/admin/docs/11-upgrade-guide.md" />

<Footer />
