digital-creative/nova-welcome-card
Add a customizable welcome card to your Laravel Nova dashboard. digital-creative/nova-welcome-card lets you display a friendly greeting, supporting copy, and helpful quick links so users can get oriented and start working faster. Easy to configure and designed to fit Nova's UI.
digital-creative/nova-welcome-card is a drop-in replacement for Nova's built-in Laravel\Nova\Cards\Help card, with the content (title, body, links) exposed as a fluent API instead of one opaque Markdown string. Day to day, you use it exactly once per project: to put a branded "start here" panel on the dashboard.
1. Install
composer require digital-creative/nova-welcome-card
Check the repo's dist/ folder first — if it ships compiled assets, no publish step is needed (the service provider registers them). If the README mentions vendor:publish, run it with the tag it documents:
php artisan vendor:publish --provider="DigitalCreative\NovaWelcomeCard\CardServiceProvider"
2. Where to look first
src/NovaWelcomeCard.php (or similarly named class) — this is the whole API surface. Read every public function returning $this; that's your configurable option list. Treat the README as a hint, the class as the source of truth.resources/js/components/*.vue — tells you what the card actually renders and which meta keys it reads. If a fluent setter only writes withMeta('foo', ...), the Vue file confirms how foo is consumed.composer.json — confirm the Nova version constraint before upgrading Nova; these thin wrapper cards break silently when Nova renames CSS classes or meta keys.3. First use case
Swap Nova's default Help card for this one on your main dashboard so every new developer/teammate lands on the same set of links (docs, staging URL, Slack channel, changelog):
// app/Nova/Dashboards/Main.php (Nova 4/5)
use DigitalCreative\NovaWelcomeCard\NovaWelcomeCard;
public function cards(): array
{
return [
NovaWelcomeCard::make()
->title('Welcome to ' . config('app.name'))
->text('Start with the onboarding doc, then grab a ticket from the board.')
->links([
'Onboarding doc' => 'https://notion.so/…',
'Staging' => 'https://staging.example.com',
'Changelog' => '/nova/resources/changelogs',
])
->width('1/2'),
// …your metrics cards
];
}
If you're still on a single NovaServiceProvider::cards() array (Nova 3 style or a custom dashboard), the same block goes there verbatim.
Replace, don't duplicate. The card exists to absorb Nova's default Help card. Whenever you see Laravel\Nova\Cards\Help::make() in a dashboard, delete it — two "help" panels on one dashboard is the most common review comment you'll get.
Environment-aware content. The most valuable pattern: point links at the right place per environment without touching the dashboard class.
NovaWelcomeCard::make()
->title('Welcome')
->links(array_filter([
'Production' => app()->isProduction() ? 'https://app.example.com' : null,
'Staging' => 'https://staging.example.com',
'Local docs' => app()->isLocal() ? '/docs/README.md' : null,
]))
->canSee(fn ($request) => $request->user()->isInternal());
Per-audience cards. Register one card per role rather than one crowded card, and gate each with canSee(). Nova evaluates canSee server-side per request, so the dashboard payload stays clean (no hidden link leakage in the nova-api/dashboards/{key} response).
NovaWelcomeCard::make()->title('Admin runbook')->links([...])->canSee(fn ($r) => $r->user()->admin),
NovaWelcomeCard::make()->title('Your first week')->links([...])->canSee(fn ($r) => $r->user()->isNew()),
Keep long content out of the dashboard class. If the setter accepts Markdown/HTML strings, load them from a config file or a resources/views/partials/welcome.md and render once:
->text(Str::markdown(file_get_contents(resource_path('markdown/welcome.md'))))
That keeps copy edits in a PR reviewable by non-developers and out of PHP.
Wrap it in your own class. With 5 stars and 0 dependents, assume you may outgrow or need to patch it. Subclass once so a future swap is a one-file change:
// app/Nova/Cards/Welcome.php
class Welcome extends NovaWelcomeCard
{
public function __construct()
{
parent::__construct();
$this->title(config('app.name'))->width('1/2');
}
}
Localization. Anything user-facing here is dashboard chrome — wrap titles and link labels in __('nova.welcome.*') so nova:translate / your lang files cover it. Fluent setters that take plain strings are easy to forget.
Compose with metrics. Put the welcome card first at 1/2 or 1/3 width, then let value/metric cards fill the rest. Nova lays cards out in insertion order per row; the welcome card is the natural top-left anchor.
Smoke-test it. Add one feature test asserting the card renders for a user and is absent for a user without permission:
$this->actingAs($user)->get('/nova-api/dashboards/main')
->assertOk()
->assertSee('Welcome');
That single test catches 90% of the breakage you'll hit on Nova upgrades.
Dashboard class returned by NovaServiceProvider::dashboards() (usually Main). Adding the card to cards() when your users land on Main via the dashboard class does nothing. Verify which dashboard is the default for the user's role.canSee runs per request. Don't put a DB query in the closure without eager-loading or caching — Nova may resolve cards more than once per page load (dashboard fetch + menu). Cache the role lookup on the user model.<script>, style="...", and sometimes even <a target="_blank"> get stripped. If your links don't render as links, you're passing HTML where Markdown is expected — use [label](url) syntax or the package's dedicated links() setter.{{ $var }} in your copy may be compiled away or throw. Escape braces and @ signs in code samples you put in the card body.var(--colors-primary-500), var(--colors-gray-500)) rather than hex values, or the card will look fine in light mode and unreadable in dark mode.composer update, run php artisan nova:publish --force and php artisan view:clear. In production, a stale public/vendor/nova or CDN-cached dist/js is almost always the culprit.^1.0 in composer.json (not *) plus a lock file is the difference between a routine update and a broken dashboard. Watch the repo's issues after each Nova major release.nova-api/dashboards/{key} JSON response — the card's component and meta are visible there. If the card is in the payload but invisible on screen, it's a Vue/CSS problem; if it's absent, it's canSee or dashboard registration.1/2, 1/3, 1/4, and full. Anything else silently collapses to full and will look odd next to metric cards — which are typically 1/3.vendor/. If it doesn't, override component() in a subclass and point at your own Vue component — Nova will resolve it from resources/js/components in your app.Help card with a Markdown string covers 80% of this functionality, and a 30-line custom Card subclass covers the rest. Don't let a 5-star convenience wrapper become load-bearing infrastructure.How can I help you explore Laravel packages today?