diff --git a/README.md b/README.md
index eca1100..bd990b0 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,127 @@
# Tatter\Menus
Dynamic menus for CodeIgniter 4
+
+[![](https://github.com/tattersoftware/codeigniter4-menus/menus/PHPUnit/badge.svg)](https://github.com/tattersoftware/codeigniter4-menus/actions?query=workflow%3A%22PHPUnit%22)
+[![](https://github.com/tattersoftware/codeigniter4-menus/menus/PHPStan/badge.svg)](https://github.com/tattersoftware/codeigniter4-menus/actions?query=workflow%3A%PHPStan%22)
+[![Coverage Status](https://coveralls.io/repos/github/tattersoftware/codeigniter4-menus/badge.svg?branch=develop)](https://coveralls.io/github/tattersoftware/codeigniter4-menus?branch=develop)
+
+## Quick Start
+
+1. Install with Composer: `> composer require tatter/menus`
+2. Create your menus by extending `Tatter\Menus\Menu`
+3. Add your menu aliases to `Config\Menus`
+4. Apply `MenuFilter` to all routes that need menus
+
+## Features
+
+**Menus** provides dynamic menus across your application. **Menus** organizes and injects the
+menu content, so you can focus on building.
+
+## Installation
+
+Install easily via Composer to take advantage of CodeIgniter 4's autoloading capabilities
+and always be up-to-date:
+* `> composer require tatter/menus`
+
+Or, install manually by downloading the source files and adding the directory to
+`app/Config/Autoload.php`.
+
+## Configuration (optional)
+
+The library's default behavior can be altered by extending its config file. Copy
+**examples/Menus.php** to **app/Config/** and follow the instructions
+in the comments. If no config file is found in app/Config the library will use its own.
+
+## Usage
+
+### Building
+
+**Menus** is built on `Spatie\Menu` with all of its wonderful, dynamic and fluent functionality.
+Use their documentation to craft your menus as simple or complex as you like:
+* [Version 2](https://spatie.be/docs/menu/v2)
+* [Version 3](https://spatie.be/docs/menu/v3) (PHP 8 only)
+
+Create your menus by extending `Tatter\Menus\Menu`. You will notice in the source code that
+`Menu` requires you to provide one method: `public function get(): string;`. You may use the
+supplied `$builder` property to access the underlying `Spatie\Menu` to build your menu,
+or provide your own HTML code or `view()` return. Some examples:
+```
+class MainMenu extends \Tatter\Menus\Menu
+{
+ public function get(): string
+ {
+ return $this->builder
+ ->link('/', 'Home')
+ ->link('/about', 'About')
+ ->html('
')
+ ->link('/contact', 'Contact')
+ ->render();
+ }
+}
+
+class FruitMenu extends \Tatter\Menus\Menu
+{
+ public function get(): string
+ {
+ return view('menus/fruit', ['active' => 'banana']);
+ }
+}
+```
+
+### Deploying
+
+Since `Menu->get()` returns a `string` it can be used in your view or layout files as is.
+However, **Menus** also comes with a [Controller Filter](https://codeigniter4.github.io/CodeIgniter4/incoming/filters.html)
+that you can use to inject menu content directly into your responses. First you need to create
+an alias for each `Menu` class you would like to use. Create **app/Config/Menus.php** (or
+start with a copy from the **examples** folder) and add your menu classes to the `$aliases`
+array. For example:
+```
+class Menus extends \Tatter\Menus\Config\Menus
+{
+ /**
+ * Menu class aliases.
+ *
+ * @var array
+ */
+ public $aliases = [
+ 'main' => \App\Menus\MainMenu::class,
+ 'fruit' => \ShopModule\FruitMenu::class,
+ ];
+}
+```
+
+Once aliases are set up you can pass them as an argument to the `MenuFilter` for any route:
+```
+$routes->add('shop/(:any)', 'ShopModule\ShopController::show/$1', ['filter' => 'menus:fruit']);
+```
+
+Then in your view or layout put the placeholder token with the name of the alias target in
+double curly braces:
+```
+
+
+ {{main}}
+ Fruit Shop
+ {{fruit}}
+...
+```
+
+Note that sometimes it is preferable to apply the filter in bulk using **app/Config/Filters.php**.
+Unfortunately parameters are [not yet supported](https://github.com/codeigniter4/CodeIgniter4/issues/2078)
+in `Config\Filters`, but you can work around this by creating your own parameter-specific Filter:
+```
+
+ */
+ public $aliases = [];
+}
diff --git a/src/Config/Filters.php b/src/Config/Filters.php
new file mode 100644
index 0000000..ad3ad0a
--- /dev/null
+++ b/src/Config/Filters.php
@@ -0,0 +1,9 @@
+aliases['menus'] = MenusFilter::class;
diff --git a/tests/filter/FilterDiscoveryTest.php b/tests/filter/FilterDiscoveryTest.php
new file mode 100644
index 0000000..a71c268
--- /dev/null
+++ b/tests/filter/FilterDiscoveryTest.php
@@ -0,0 +1,26 @@
+initialize();
+
+ $result = $this->getPrivateProperty($filters, 'config');
+
+ $this->assertArrayHasKey('menus', $result->aliases);
+ $this->assertSame(MenusFilter::class, $result->aliases['menus']);
+ }
+}
diff --git a/tests/filter/FilterSuccessTest.php b/tests/filter/FilterSuccessTest.php
deleted file mode 100644
index 8ec0477..0000000
--- a/tests/filter/FilterSuccessTest.php
+++ /dev/null
@@ -1,34 +0,0 @@
-response->setBody('{{test}}');
- $this->response->setHeader('Content-Type', 'text/html');
- $this->caller = $this->getFilterCaller(MenusFilter::class, 'after');
- }
-
- public function testValid()
- {
- $result = ($this->caller)(['test']);
- $this->assertInstanceOf(ResponseInterface::class, $result);
-
- $body = $result->getBody();
- $this->assertSame('bananas', $body);
- }
-}
diff --git a/tests/filter/FilterFailureTest.php b/tests/filter/MenusFilterTest.php
similarity index 89%
rename from tests/filter/FilterFailureTest.php
rename to tests/filter/MenusFilterTest.php
index 1f60dc4..09cf49d 100644
--- a/tests/filter/FilterFailureTest.php
+++ b/tests/filter/MenusFilterTest.php
@@ -5,7 +5,7 @@
use Tatter\Menus\Filters\MenusFilter;
use Tests\Support\MenusTestCase;
-class FilterFailureTest extends MenusTestCase
+class MenusFilterTest extends MenusTestCase
{
use FilterTestTrait;
@@ -87,4 +87,13 @@ public function testMissingPlaceholder()
$caller(['test']);
}
+
+ public function testValid()
+ {
+ $result = ($this->caller)(['test']);
+ $this->assertInstanceOf(ResponseInterface::class, $result);
+
+ $body = $result->getBody();
+ $this->assertSame('bananas', $body);
+ }
}