🐘 Jumbo
PHP 8.3+ Built on Dumbo 🐘 MIT licensed

Jumbo

Content sites in plain PHP.

Dumbo's mum. Bigger ears.

composer global require notrab/jumbo
pages/blog/[slug].php
<?php

use Dumbo\Jumbo\Page;

return new Page(
    paths: fn($site) => array_map(
        fn($slug) => ["slug" => $slug],
        $site->slugs(),
    ),
    render: fn($context, $site) => $site->view("post", [
        "post" => $site->find($context->req->param("slug")),
    ]),
);

Pick something to build

Each of these is a few files in a Jumbo site. Run jumbo new, add one, and you're away.

A blog with a feed

Posts in Markdown, a page for each one, and an RSS feed from the same list. Code is highlighted on the server.

// pages/rss.xml.php
return new Page(render: fn($c, $site) => $site->rss(
    array_map(fn($post) => [
        'title' => $post['title'],
        'link' => "/blog/{$post['slug']}",
        'date' => $post['meta']['date'],
    ], $site->posts())
));
Feeds and sitemaps →

A docs site, like this one

Markdown pages with a sidebar, components for callouts and tabs, and a table of contents from the headings.

# Routing

::: callout type="tip"
Files in pages/ are **routes**.
:::

```php
$site->find('routing')['toc'];
```
Content →

A landing page with a contact form

The page handles its own form. Validation errors show next to the fields, and a good post redirects.

actions: [
    'default' => function ($input, $c) {
        if (empty($input['email'])) {
            return Action::fail(['email' => 'Required']);
        }

        return $c->redirect('/thanks', 303);
    },
],
Actions →

A site in three languages

Every page is served under each locale, with translated Markdown and hreflang links.

'i18n' => ['locales' => ['en', 'fr', 'es']],

$site->find('home', $site->locale($c));
$site->alternates($c);
// ['en' => '/', 'fr' => '/fr', 'es' => '/es']
Locales →

A portfolio full of photos

Responsive images resized with GD, in WebP, with their sizes set so nothing jumps as they load.

<?= $site->img('/work/poster.jpg', 'Gig poster',
    width: 800,
) ?>
Images →

A static site with a live corner

Build most pages for a CDN, and render search or a dashboard on each request with Dumbo middleware in front.

return new Page(
    prerender: false,
    render: fn($c, $site) => $site->view('search', [
        'q' => $c->req->query('q'),
    ]),
);
Page data →
🎪

Put on your first show in about a minute

Install Jumbo, run jumbo new, and open your browser.

Quickstart →