diff --git a/README.md b/README.md
index 2600806..58434d5 100644
--- a/README.md
+++ b/README.md
@@ -166,3 +166,204 @@ export default (foo: string, bar: number) => {
}
}
```
+
+# Real-world example
+
+Here's a practical example that uses constructors, `init()`, refs, events, and includes dependencies in the right way.
+
+This example uses the Alpine component that we use for search on the [Lean documentation site](https://lean-admin.dev).
+
+
+resources/js/app.ts
+
+```ts
+declare global {
+ interface Window {
+ Alpine: any;
+ }
+}
+
+import { component } from '@leanadmin/alpine-typescript';
+import Search from './search';
+
+component('search', Search);
+
+import 'alpinejs';
+```
+
+
+
+**`app.ts` highlights:**
+- It's a good idea to declare the `Alpine` property on `Window` in case you need to use `window.Alpine`
+- We initialize each component by calling `component()`
+- We import Alpine *after* this package
+
+
+resources/js/search.js
+
+```ts
+import { AlpineComponent } from '@leanadmin/alpine-typescript';
+
+type AlgoliaIndex = {
+ search: Function,
+};
+
+type Result = any;
+
+export default class Search extends AlpineComponent {
+ search: string = '';
+ results: Result[] = [];
+
+ constructor(
+ public index: AlgoliaIndex,
+ ) {
+ super();
+ }
+
+ previousResult(): void {
+ let result = this.currentResult();
+
+ if (! result) {
+ if (this.results.length) {
+ // First result
+ this.getResult(0).focus();
+ } else if (this.search.length) {
+ // Re-fetch results
+ this.queryAlgolia();
+ }
+
+ return;
+ }
+
+ if (result.previousElementSibling instanceof HTMLElement && result.previousElementSibling.tagName === 'A') {
+ (result.previousElementSibling).focus();
+ } else {
+ // Last result
+ this.getResult(this.results.length - 1).focus();
+ }
+ };
+
+ nextResult(): void {
+ let result = this.currentResult();
+
+ if (! result) {
+ if (this.results.length) {
+ // First result
+ this.getResult(0).focus();
+ } else if (this.search.length) {
+ // Re-fetch results
+ this.queryAlgolia();
+ }
+
+ return;
+ }
+
+ if (result.nextElementSibling instanceof HTMLElement) {
+ result.nextElementSibling.focus();
+ } else {
+ // First result
+ this.getResult(0).focus();
+ }
+ };
+
+ getResult(index: number): HTMLElement {
+ return this.$refs.results.children[index + 1] as HTMLElement;
+ };
+
+ currentResult(): HTMLElement {
+ if (! this.$refs.results.contains(document.activeElement)) {
+ return null;
+ }
+
+ return document.activeElement as HTMLElement;
+ };
+
+ queryAlgolia(): void {
+ if (this.search) {
+ this.index.search(this.search, {
+ hitsPerPage: 3,
+ }).then(({ hits }) => {
+ this.results = hits.filter((hit: Result) => {
+ // Remove duplicate results
+ const occurances: any[] = hits.filter((h: Result) => h.hierarchy.lvl1 === hit.hierarchy.lvl1);
+
+ return occurances.length === 1;
+ });
+
+ this.results.forEach((result: Result) => {
+ // Clean displayed text
+ if (result._highlightResult && result._highlightResult.content) {
+ return result._highlightResult.content.value.replace(' ', '');
+ }
+ });
+
+ if (this.results.length) {
+ this.$nextTick(() => this.getResult(0).focus());
+ }
+ })
+ } else {
+ this.results = [];
+
+ this.$refs.search.focus();
+ }
+ };
+
+ init(): void {
+ this.$watch('search', () => this.queryAlgolia());
+ }
+}
+```
+
+
+
+**`search.js` highlights:**
+- We `export default` the class
+- We have to call `super()` if we define a constructor
+- Sometimes, we have to use `as HTMLElement` because the DOM API can return `Element` which doesn't have methods like `focus()`
+- We define an `init()` method and can access magic properties there
+- We create helper types for consistency among parameters and return types, even if the type is `any` because we don't know much about the structure. Especially useful for API calls.
+
+
+page.blade.php
+
+```html
+
+```
+
+
+
+**`page.blade.php` highlights:**
+- We call the component in `x-data`
+- We use both the constructor and `init`. The constructor cannot access magic properties, `init` can.
+- We can still use Alpine syntax in the template with no issues