Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Nova Text Card Laravel Package

ericlagarda/nova-text-card

A simple Laravel Nova card for displaying custom text, HTML, or notes directly on your dashboard, resource pages, or detail screens—useful for instructions, summaries, and lightweight static content without building a custom Vue component.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register

composer require ericlagarda/nova-text-card

The package uses Composer auto-discovery, so EricLagarda\NovaTextCard\CardServiceProvider should land in bootstrap/cache/packages.php on its own. Verify before doing anything else:

php artisan about   # or grep the package name in bootstrap/cache/packages.php

Register the card from app/Providers/NovaServiceProvider.php — either globally in cards() or per-dashboard in dashboards():

use EricLagarda\NovaTextCard\TextCard;

public function cards()
{
    return [
        (new TextCard)
            ->title('Welcome')
            ->content('Some **text** for the dashboard'),
    ];
}

Where to look first — in this order

  • README.md — the fluent API contract (title(), content(), markdown/HTML flags, alignment). Author against this, not against guesses.
  • src/TextCard.php — every chainable option lives here. Read it end to end; it's short.
  • resources/js/components/Card.vue — what actually renders, and whether content goes through v-html or interpolation. This determines your safety posture.
  • resources/js/card.js and webpack.mix.js — the asset entrypoint and build pipeline. Nova 4+ reworked this, so inspect it before assuming anything works.
  • composer.json — Nova/PHP/Laravel constraints. Last release was June 2020; assume Nova 2/3-era assumptions until proven otherwise.

First use case: a "how to use this panel" card

Put a single static text card at the top of the main dashboard. It's the lowest-risk way to prove compatibility on your Nova version before wiring the card anywhere load-bearing — no data source, no HTML, no user input. If that renders, you know the provider registered, the compiled JS loaded, and the Vue component mounts. Everything after that is content.

Implementation Patterns

Config-driven announcements (the highest-value pattern)

Keep copy out of PHP so non-developers can change it without a deploy:

(new TextCard)
    ->title(config('admin.notice_title'))
    ->content(config('admin.notice_body')),

One deployable card, many messages — maintenance windows, policy changes, onboarding notes. Toggle visibility with canSee() or by returning the card conditionally from cards().

Markdown for anything you'd version-control

Runbook pointers, onboarding checklists, changelog snippets. Prefer markdown mode over raw HTML: safer to author, easier to diff, and it keeps you out of the v-html blast radius. Store the source in a config key, a translation file, or a resources/views/admin/ partial and read it in.

HTML only for content you own

<ul> quick-links, small CTAs, a runbook link list. Restrict HTML mode to hardcoded strings, config, and translation keys. Anything user-sourced — model fields, API responses, ticket text — must be sanitized before it reaches HTML mode. The package is a convenience, not a security boundary.

Compose a lightweight "admin home"

Treat it like any Nova card and lay out a row:

(new TextCard)->title('Quick links')->content($links)->width('1/2'),
(new TextCard)->title('Support')->content($support)->width('1/2'),

Place this under your metrics cards and you've built a dashboard section without writing a Vue component. Inherit canSee() from Nova's Card to scope guidance by role — ops sees a runbook card, finance sees billing notes, nobody else sees either.

Resource detail banners — where it earns its keep

Override cards() on a Resource, or scope conditionally in NovaServiceProvider::cards(), to explain non-obvious state right next to the fields it affects:

(new TextCard)
    ->title('Read-only record')
    ->content('This record is synced from the billing system; edits will be overwritten on the next sync.'),

This is the use case a generic dashboard widget can't match: contextual explanation adjacent to the form it's about.

Computed content via a cached service

The card is plain PHP, so you can inject a computed summary:

(new TextCard)
    ->title('Sync status')
    ->content(Cache::remember('sync.summary', 300, fn () => $this->syncSummary())),

Keep syncSummary() cheap. See the gotcha below on N+1 — cards resolve on every dashboard and detail request.

Gotchas and Tips

Nova version drift is the dominant risk. Last release was mid-2020. Nova 4+ moved to Vue 3 and a revised nova.mix asset pipeline, and Nova 2/3 cards frequently need Card.vue rebuilt and the service provider adjusted to register assets the Nova 4 way. Time-box a proof of concept in a disposable Nova install before touching production. Plan on that check failing.

Fork rather than depend. With 20 stars, zero dependents, and a June 2020 release, you become the maintainer the moment you install it. The realistic play is to copy src/ and resources/js/ into nova-components/text-card/ (or a packages/ path repository) and own it. That removes Composer and upgrade risk for a feature that is roughly 40 lines of PHP plus a tiny Vue component.

Blank card almost always means assets, not PHP. If the card registers but renders nothing, check that compiled dist/js/card.js exists and is actually loaded — browser devtools Network tab first, then Nova's provider asset registration. A stale or missing build is the usual culprit.

Confirm HTML escaping in Card.vue. Read whether content is rendered with v-html or interpolated. If it's v-html, never pass unsanitized input, and audit anything sourced from users, models, or APIs before enabling HTML mode.

Pin exact versions. In composer.json, pin the card and its transitive dependencies. A floating constraint will break on the next composer update with no upstream fix coming.

Don't query inside content(). Cards resolve on every request to the dashboard and every resource detail page. A query or API call there multiplies across every load — cache with a short TTL or precompute the value at boot.

Extension points are limited by design. Beyond the fluent options in TextCard.php and a forked Card.vue, there is nothing to hook into. Theming parity with Nova 4/5, dark mode support, and localization all have to live in your fork — budget for that if you need any of them.

Debugging checklist, in order:

  1. Composer discovery ran — check bootstrap/cache/packages.php or php artisan about.
  2. The card is actually returned from cards() or the dashboard's dashboards().
  3. Compiled JS is loaded in the browser (Network tab).
  4. Card.vue renders your content flag correctly — markdown mode versus HTML mode.
  5. Nova version matches the constraint in composer.json.

Most "card is blank" reports are a failure at step 1 or 3, not step 4. Work the list in order rather than jumping to the component.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource