The App Panel
The app panel at /app is where an application built on the starter does its work. It is the default Filament panel, configured in App\Providers\Filament\AppPanelProvider, and any signed-in user can open it. API users can’t: they authenticate through OAuth clients and never have a panel session. Back-office tools belong in the administration panel instead.
The panel also owns sign-in for the whole application. Guests of any panel are sent to /app/login (see Authentication), and in a locked-down environment users without an application role are sent to /app/access-restricted (see Environment Lockdown).
Where Its Code Lives
Section titled “Where Its Code Lives”The panel discovers Resources, Pages, Clusters and Widgets in app/Filament/App/:
Directoryapp/Filament/App/
DirectoryClusters/
- AccountCluster.php
DirectoryAccountCluster/Pages/
- AccessTokens.php
- ConnectedApplications.php
- Preferences.php
- Profile.php
DirectoryLivewire/
- AnnouncementBanner.php
DirectoryPages/ discovered, empty until you add one
- …
DirectoryResources/ discovered, empty until you add one
- …
DirectoryStarter/Pages/ the starter’s own pages
- Announcements.php
DirectoryAuth/ sign-in pages
- …
- Concerns/HasSiteHeader.php
- ComponentGallery.php
- ContactSupport.php
- EnvironmentLockdown.php
DirectoryWidgets/
- YourAccountWidget.php
The sidebar is for your application’s features. The starter ships only the dashboard and, outside production, the component gallery there; Contact Support is in the Help menu.
Filament’s generators target the default panel, so new resources and pages land here without a --panel option:
php artisan make:filament-resource Project --generate --model-namespace=App\\Domains\\Project\\Modelsphp artisan make:filament-page ReportsViews for the panel’s pages go in resources/views/filament/app/. The panel’s Tailwind theme, resources/css/filament/app/theme.css, compiles classes from every file the repository tracks; only a package’s views under vendor/, which git ignores, need an @source line, as Filament’s have.
The Dashboard
Section titled “The Dashboard”The panel’s home page at /app is Filament’s own Filament\Pages\Dashboard, registered with ->pages() in AppPanelProvider. It renders the widgets in app/Filament/App/Widgets/.
The starter ships one, YourAccountWidget, which shows the signed-in user:
- a greeting: “Welcome” at the first sign-in, “Welcome Back” after it
- the roles they hold beyond the default Northwestern User role
- an Account link to the Account area
- links to Contact Support and the documentation, each shown only when it is configured, as in the Help menu
Its view is resources/views/filament/app/widgets/your-account.blade.php. Keep it, change it, or delete it as the application grows its own dashboard.
Add widgets with php artisan make:filament-widget. For anything a grid of widgets can’t hold, write your own page that extends Filament\Pages\Dashboard in app/Filament/App/Pages/ and register it in ->pages() instead.
Announcements
Section titled “Announcements”Every page shows the most important live announcement the user hasn’t dismissed in a banner above its heading, through the PAGE_START render hook in AppPanelProvider (App\Filament\App\Livewire\AnnouncementBanner). /app/announcements lists every announcement they can see, reached from the banner and the Help menu.
The Account Area
Section titled “The Account Area”The Account area at /app/account is each user’s own: who they are, their preferences, the applications connected to their account and, for those allowed, their personal access tokens. It is a cluster, App\Filament\App\Clusters\AccountCluster, with its pages as tabs across the top. It isn’t in the sidebar, which is for your application’s features; users reach it from the user menu’s Account item and the dashboard widget’s Account link. Every user who can open the app panel can open it.
Profile
Section titled “Profile”/app/account/profile is read-only:
- Details. For a NetID user: name, NetID, email, primary affiliation, job titles and departments, which come from the Northwestern Directory and refresh at each sign-in. For an email user: the name, email, title and department an administrator entered, with a Request a Change link to Contact Support when support tickets are enabled.
- Roles. The roles they hold beyond the default Northwestern User role.
Access Tokens
Section titled “Access Tokens”/app/account/access-tokens lists the user’s personal access tokens and creates and revokes them. It shows while the API is on (API_ENABLED) to people other than API users who hold the CreatePersonalAccessTokens permission; of the default roles, only Super Administrator has it. A new token is shown once.
Connected Applications
Section titled “Connected Applications”/app/account/connected-applications lists the applications the user has allowed to act for them, one row per application, with what each may do, when it connected and when it was last used. Disconnect removes one application’s access and Disconnect All removes every application’s. Every user can open it while the API or the MCP server is enabled; AI clients are labelled as such, with a note that their names aren’t verified.
Preferences
Section titled “Preferences”/app/account/preferences is where users change their own settings. The starter ships one, their timezone, which every date in both panels is shown in (see Framework Defaults). Directory sync sets a new user’s timezone to DEFAULT_USER_TIMEZONE and leaves it alone after that. Users also choose whether to be emailed announcements and when an application connects to their account (they’re always told in the notification bell), and those who can hold personal access tokens whether to be emailed before one expires.
An administrator impersonating a user sees that user’s preferences but can’t save them: the page hides Save, and save() refuses with a 403.
Adding a Preference
Section titled “Adding a Preference”Everything else a user chooses goes in the users.preferences JSON column, read through a typed class, App\Domains\User\Data\UserPreferences. It ships with three: emailBeforeAccessTokensExpire, emailWhenApplicationConnects and emailAnnouncements. Each preference is a promoted constructor property with a default, so adding one needs no migration:
-
Add the property:
app/Domains/User/Data/UserPreferences.php final readonly class UserPreferences extends Preferences{public function __construct(public bool $emailBeforeAccessTokensExpire = true,public bool $emailWhenApplicationConnects = true,public bool $emailAnnouncements = true,public bool $emailWeeklySummary = true,) {}} -
Add its field to the form in
App\Filament\App\Clusters\AccountCluster\Pages\Preferences, named after the property:Toggle::make('emailWeeklySummary')->label('Weekly summary')->helperText('A digest of the week\'s activity, every Monday.'),The page fills the form from
$user->preferencesand saves every field excepttimezoneback into it. -
Read it wherever it matters:
if ($user->preferences->emailWeeklySummary) {// ...}
Properties can be bool, int, float, string, array or a backed enum, and may be nullable. Reading is forgiving, because stored JSON outlives code: a key with no matching property is dropped, and a missing or wrongly typed value gets its default. To change preferences in code, use with(), which returns a copy:
$user->preferences = $user->preferences->with(['emailWeeklySummary' => false]);$user->save();The base class, App\Domains\Core\Data\Preferences, works for any model: extend it and cast a JSON column with your subclass ('settings' => TeamSettings::class).
The Component Gallery
Section titled “The Component Gallery”/app/gallery shows Filament’s common components in the Northwestern theme with sample content: buttons, badges, callouts, form fields, a table, notifications and modals. It is in the sidebar outside production. ComponentGallery::canAccess() refuses it in production, which also hides its sidebar item.
Delete app/Filament/App/Starter/Pages/ComponentGallery.php when you no longer need it.
The Top Bar
Section titled “The Top Bar”The top bar holds what every page shares: the Northwestern wordmark, the application name (<x-panel-brand>), the environment badge outside production, the Help menu, the notification bell and the user menu.
The Help Menu
Section titled “The Help Menu”<x-help-menu> (resources/views/components/help-menu.blade.php) is rendered in the app panel’s top bar, just ahead of the user menu, and in the header of public pages. The administration panel doesn’t show it. Each item shows only when it is available:
- Announcements, for signed-in users: the Announcements page.
- Changelog, when the changelog is enabled.
- Contact Support, for signed-in users when support tickets are enabled.
- Documentation, when
SUPPORT_DOCUMENTATION_URLis set. It opens in a new tab.
The menu disappears when none of them apply. To add an item, add an entry to the $items array, or null when it shouldn’t show:
Route::has('policies.index') ? [ 'label' => 'Policies', 'url' => route('policies.index'), 'icon' => Heroicon::OutlinedDocumentText, 'external' => false, ] : null,The component also renders on error pages, so keep anything that could query the database inside rescue(), as the Help menu’s signed-in check does.
The User Menu
Section titled “The User Menu”The user menu has three items, set in AppPanelProvider::userMenuItems():
- Account links to the Account area.
- Administration links to
/administration. It shows only to users who can open that panel (User::canAccessPanel()). - Sign Out goes to the
logoutroute.
Notifications
Section titled “Notifications”The bell in the top bar lists database notifications, refreshed every 30 seconds (->databaseNotificationsPolling('30s')). Notifications are stored in the notifications table, created by 2026_01_22_232242_create_notifications_table.php. The administration panel has the same bell.
Send one with Filament’s notification builder:
use Filament\Notifications\Notification;
Notification::make() ->title('Report ready') ->body('Your quarterly report finished processing.') ->success() ->sendToDatabase($user);A Laravel notification class can send the same notification alongside mail or other channels. Return Filament’s database message from toDatabase(), so the bell can render it:
use Filament\Notifications\Notification as FilamentNotification;use Illuminate\Notifications\Notification;
class ReportReady extends Notification{ public function via(object $notifiable): array { return ['database', 'mail']; }
public function toDatabase(object $notifiable): array { return FilamentNotification::make() ->title('Report ready') ->getDatabaseMessage(); }}The bell picks up new notifications at its next poll. A Livewire component in the panel can refresh it at once with $this->dispatch('databaseNotificationsSent'), as the component gallery’s “Send Me a Notification” button does.
Simple Pages
Section titled “Simple Pages”Simple pages (Filament’s SimplePage) render a single card without the sidebar. The sign-in pages and the lockdown page are simple pages. The app panel renders <x-site-header> above every simple page’s card, so they share the header of public and error pages.
-
Extend
Filament\Pages\SimplePageinapp/Filament/App/Pages/, and useApp\Filament\App\Starter\Pages\Concerns\HasSiteHeader. The trait turns off Filament’s own logo and simple-page user menu, which would repeat what the header shows.use App\Filament\App\Starter\Pages\Concerns\HasSiteHeader;use Filament\Pages\SimplePage;use Filament\Schemas\Schema;class Welcome extends SimplePage{use HasSiteHeader;public function content(Schema $schema): Schema{return $schema->components([// ...]);}} -
Register its route in
AppPanelProvider::panel(). Filament’s page discovery only picks up full pages, so simple pages are routed in->routes(), as the lockdown page is:->routes(function (): void {Route::get('welcome', Welcome::class)->middleware(Authenticate::class)->name('welcome');})