PHPackages                             do-it-s/dropin-markdown - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. do-it-s/dropin-markdown

ActiveLibrary

do-it-s/dropin-markdown
=======================

Drop-in markdown-file-backed static pages and sysadmin announcements for Laravel, with GFM + GitHub-style alert rendering.

v0.1.0(yesterday)03↓50%MITPHPPHP ^8.3

Since Aug 16Pushed yesterdayCompare

[ Source](https://github.com/do-it-s/dropin-markdown)[ Packagist](https://packagist.org/packages/do-it-s/dropin-markdown)[ RSS](/packages/do-it-s-dropin-markdown/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (8)Versions (2)Used By (0)

dropin-markdown
===============

[](#dropin-markdown)

A Laravel package for drop-in, markdown-file-backed static pages and sysadmin announcements, with GFM + GitHub-style alert (`[!NOTE]`, `[!WARNING]`, etc.) rendering. Content lives entirely in `.md` files on disk — no database registration or config edits needed to add or edit a page or announcement, only to place a file.

Requirements
------------

[](#requirements)

- PHP ^8.3, Laravel ^13.0
- `pomodocs/commonmark-alert` ^0.8.0 (pulled in automatically by Composer)

Installation
------------

[](#installation)

### 1. Add the dependency via Composer

[](#1-add-the-dependency-via-composer)

```
composer require do-it-s/dropin-markdown

```

### 2. Run migrations

[](#2-run-migrations)

```
php artisan migrate

```

This creates `announcement_acknowledgements`, which tracks each user's per-announcement read/consent state. Announcement content itself is never stored in the database.

### 3. Provide `resources/views/layouts/app.blade.php`

[](#3-provide-resourcesviewslayoutsappbladephp)

Every view in this package does `@extends('layouts.app')`. At minimum, the app's layout must satisfy:

ElementReason`@yield('content')`Where each view's `@section('content')` is injected``Required by the `@csrf` field in the announcement acknowledgement formHeader navigation, branding, and the choice of UI framework are otherwise entirely up to the app.

### 4. Provide a `home` route

[](#4-provide-a-home-route)

`AnnouncementController::acknowledge()` falls back to `redirect()->route('home')` when the `return_to` parameter (see "Announcements" below) is missing, tampered with, or names a route that no longer exists. This package does not define a route named `home` itself, so the app must provide one.

Static pages
------------

[](#static-pages)

Placing a file at `resources/markdown/pages/{slug}.md` makes it available at `/pages/{slug}` (route name `pages.show`), rendered through `dropin-markdown::pages.show`. The slug may contain slashes, so pages can be nested: `resources/markdown/pages/manual/upload.md` maps to `/pages/manual/upload`.

There is no listing/index of pages — the app links to whichever slugs it wants to expose.

Announcements
-------------

[](#announcements)

Placing a file at `resources/markdown/announcements/{slug}.md` registers it as an announcement, viewable (while signed in) at `/announcements/{slug}` (route name `announcements.show`). Each file starts with an optional YAML-ish front matter block:

```
---
title: Scheduled Maintenance
consent_label: I have read and agree to the above
blocking: true
cooldown: 30m
close_label: Got it
---

Service will be unavailable on 2026-09-01 from 02:00 to 04:00 JST.
```

KeyDefaultMeaning`title`the slugHeading shown above the body`consent_label`*(none)*If set, renders a checkbox with this label; the user must check it to acknowledge. If omitted, the announcement only requires being viewed once (no checkbox).`blocking``true`If `true`, a `consent_label` announcement cannot be dismissed unconsented — the submit button stays disabled until checked. If `false`, it can be read-and-dismissed without consenting, but reappears as pending after `cooldown` elapses.`cooldown``10m`For non-blocking, consent-requiring announcements only: how long after an unconsented dismissal before the announcement is pending again. Accepts `m`/`h`/`d` (e.g. `30m`, `2h`, `1d`).`close_label`translated "Close"Submit button label### Gating routes on pending announcements

[](#gating-routes-on-pending-announcements)

The `announcements.pending` middleware alias redirects a signed-in user to their oldest unacknowledged announcement (oldest file first) instead of letting the request through:

```
Route::middleware(['auth', 'announcements.pending'])->group(function () {
    // ...
});
```

Toggle this behavior globally via `config('dropin-markdown.announcements_enabled')` (`.env`'s `DROPIN_MARKDOWN_ANNOUNCEMENTS_ENABLED`, default `true`) — host apps typically pin this to `false` in their test environment so unrelated tests aren't gated by real announcement content.

The announcement view posts back to `announcements.acknowledge` with a `return_to` field (the *name* of a route to redirect to afterward, e.g. a host app's own notification history screen) — pass it as a query parameter when linking to `announcements.show` yourself: `route('announcements.show', ['slug' => $slug, 'return_to' => 'history.index'])`. It falls back to `home` if omitted or invalid (see "Provide a `home` route" above).

Markdown rendering
------------------

[](#markdown-rendering)

`DoITs\DropinMarkdown\Support\Markdown::render(string $content): string` wraps `Illuminate\Support\Str::markdown()` with GitHub Flavored Markdown and the GitHub-style alert extension (`> [!NOTE]`, `> [!TIP]`, `> [!IMPORTANT]`, `> [!WARNING]`, `> [!CAUTION]`) always enabled. Options are configurable via `config('dropin-markdown.markdown_options')`, passed through as `Str::markdown()`'s second argument. Both built-in views (`pages.show`, `announcements.show`) use it, and apps are free to call it directly for their own ad hoc markdown-backed content (e.g. `Markdown::render(file_get_contents(resource_path('markdown/welcome.md')))`).

Customizing views
-----------------

[](#customizing-views)

```
php artisan vendor:publish --tag=dropin-markdown-views

```

Copies `pages/show.blade.php` and `announcements/show.blade.php` into `resources/views/vendor/dropin-markdown/`; from then on, the copied files take priority (standard Laravel view-namespace resolution).

Customizing routes
------------------

[](#customizing-routes)

`pages.show`, `announcements.show`, and `announcements.acknowledge` are registered automatically. If the app wants different URIs, a different middleware group, or to drop a route entirely, call `DropinMarkdown::ignoreRoutes()` from `AppServiceProvider::register()`:

```
use DoITs\DropinMarkdown\DropinMarkdown;

public function register(): void
{
    DropinMarkdown::ignoreRoutes();
}
```

Once called, this package registers no `routes/web.php` at all. From then on, the app writes its own routes pointing at this package's controllers (`DoITs\DropinMarkdown\Http\Controllers\...`), with whatever URIs and middleware it wants (copying this package's own `routes/web.php` as a starting point is the fastest way).

Customizing translated copy
---------------------------

[](#customizing-translated-copy)

View copy (the announcement heading, close button label, consent-required validation message) goes through translation keys under the `dropin-markdown::` namespace (`lang/en`, `lang/ja`). Placing a same-named file under `lang/vendor/dropin-markdown/{locale}/` overrides it automatically (standard Laravel mechanism). To get a starting template:

```
php artisan vendor:publish --tag=dropin-markdown-lang

```

Config
------

[](#config)

```
php artisan vendor:publish --tag=dropin-markdown-config

```

Publishes `config/dropin-markdown.php` (`announcements_enabled`, `markdown_options`) into the app.

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance100

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

1d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/3b75e3e8e2f4fa7ec313b4f986e3cb55e9fb24decde54bbfcb49d4f332f57502?d=identicon)[do-it-s](/maintainers/do-it-s)

---

Top Contributors

[![do-it-s](https://avatars.githubusercontent.com/u/280609089?v=4)](https://github.com/do-it-s "do-it-s (1 commits)")

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/do-it-s-dropin-markdown/health.svg)

```
[![Health](https://phpackages.com/badges/do-it-s-dropin-markdown/health.svg)](https://phpackages.com/packages/do-it-s-dropin-markdown)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.4M354](/packages/psalm-plugin-laravel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
