https://github.com/itw-creative-works/ultimate-jekyll-manager
All-in-one package for ultimate-jekyll
https://github.com/itw-creative-works/ultimate-jekyll-manager
css framework front-end html javascript ultimate-jekyll webpack website
Last synced: 1 day ago
JSON representation
All-in-one package for ultimate-jekyll
- Host: GitHub
- URL: https://github.com/itw-creative-works/ultimate-jekyll-manager
- Owner: itw-creative-works
- License: mit
- Created: 2020-09-29T21:38:47.000Z (over 5 years ago)
- Default Branch: main
- Last Pushed: 2026-03-30T23:50:59.000Z (9 days ago)
- Last Synced: 2026-04-02T06:22:26.992Z (6 days ago)
- Topics: css, framework, front-end, html, javascript, ultimate-jekyll, webpack, website
- Language: JavaScript
- Homepage: https://itwcreativeworks.com
- Size: 7.61 MB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
Site | NPM Module | GitHub Repo
Ultimate Jekyll is a template that helps you jumpstart your Jekyll sites and is fueled by an intuitive incorporation of npm, gulp, and is fully SEO optimized and blazingly fast.
## 🦄 Features
* **SEO Optimized**: Ultimate Jekyll is fully SEO optimized.
* **Blazingly Fast**: Ultimate Jekyll is blazingly fast.
* **NPM & Gulp**: Ultimate Jekyll is fueled by an intuitive incorporation of npm and gulp.
## 🚀 Getting started
1. [Create a repo](https://github.com/itw-creative-works/ultimate-jekyll/generate) from the **Ultimate Jekyll** template.
2. Clone the repo to your local machine.
3. Run these commands to get everything setup and sync'd!
```bash
npm start
```
## 📦 How to sync with the template
1. Simply run `npm start` in Terminal to get all the latest updates from the **Ultimate Jekyll template** and launch your website in the browser.
## 🌎 Publishing your website
1. Change the `url` in `_config.yml` to your domain.
2. Push your changes to GitHub using `npm run dist` in Terminal.
## ⛳️ Flags
* `--browser=false` - Disables the browser from opening when running `npm start`.
```bash
npm start -- --browser=false
```
* `--debug=true` - Enables logging of extra information when running `npm start`.
```bash
npm start -- --debug=true
```
* `--ujPluginDevMode=true` - Enables the development mode for the [Ultimate Jekyll Ruby plugin](https://github.com/itw-creative-works/jekyll-uj-powertools).
```bash
npm start -- --ujPluginDevMode=true
```
* `--profile` - Enables Jekyll build profiling to see how long each phase takes.
```bash
npm start -- --profile
```
* `--all-posts` - Disables the development post limit (15 posts) and builds with all posts. Useful when you need to test with full blog content.
```bash
npm start -- --all-posts
```
### Other ENV variables
```bash
UJ_PURGECSS=true # Enables PurgeCSS to remove unused CSS (normally only happens in production builds)
```
## Running Specific Tasks
You can run specific tasks using the `npm run gulp` command with the appropriate task name.
Some of these require environment variables to be set and other tasks to be run first.
Here are some examples:
### Run the `audit` task:
```bash
# Run the audit task
npx uj audit
# Run with a Lighthouse URL (defaults to "/" if not provided)
npx uj audit -- --lighthouseUrl="/contact"
# Add autoExit to continue developing and testing AFTER the audit
npx uj audit -- --lighthouseUrl="/contact" --autoExit=false
```
### Run the `translation` task:
```bash
# Test translation with GitHub cache (requires GH_TOKEN and GITHUB_REPOSITORY)
GH_TOKEN=XXX \
GITHUB_REPOSITORY=XXX \
UJ_TRANSLATION_CACHE=true \
npx uj translation
# Test with only 1 file
UJ_TRANSLATION_ONLY="index.html" \
GH_TOKEN=XXX \
GITHUB_REPOSITORY=XXX \
UJ_TRANSLATION_CACHE=true \
npx uj translation
```
### Run the `imagemin` task:
Test image optimization with GitHub cache in development mode:
```bash
# Test with GitHub cache (requires GH_TOKEN and GITHUB_REPOSITORY)
GH_TOKEN=XXX \
GITHUB_REPOSITORY=XXX \
UJ_IMAGEMIN_CACHE=true \
npx uj imagemin
# Or run locally without cache
npx uj imagemin
```
The imagemin task will:
- Process images from `src/assets/images/**/*.{jpg,jpeg,png}`
- Generate multiple sizes (1024px, 425px) and WebP formats
- Cache processed images in `cache-imagemin` branch (when using GitHub cache)
- Skip already processed images on subsequent runs
## 🛠 Developing
1. Clone the repo to your local machine.
2. Run these commands
```bash
npm install
npm run prepare:watch
```
### Run the `blogify` task:
Create 12 test blog posts in the `_posts` directory with the `blogify` task. This is useful for testing and development purposes.
```bash
npx uj blogify
```
## Page Frontmatter
You can add the following frontmatter to your pages to customize their behavior:
### All pages
```yaml
---
# Layout and Internals
layout: themes/[ site.theme.id ]/frontend/core/minimal # The layout to use for the page, usually 'default' or 'page'
permalink: /path/to/page # The URL path for the page, can be relative
# Control the page's meta tags
meta:
index: true # Set to false to disable indexing by search engines
title: 'Page Title' # Custom meta title for the page
description: 'Page description goes here.' # Custom meta description for the page
breadcrumb: '' # Custom breadcrumb for the page
# Control the page's theme and layout
theme:
nav:
enabled: true # Enable theme's nav on the page
footer:
enabled: true # Enable theme's footer on the page
body:
class: '' # Add custom classes to the body tag
main:
class: '' # Add custom classes to the main tag
head:
content: '' # Injected at the end of the head tag
foot:
content: '' # Injected at the end of the foot tag (inside )
---
```
### Post pages
```yaml
---
# Post pages
post:
title: "Post Title" # Custom post title for the page
description: "Post description goes here." # Custom post description for the page
author: "author-id" # ID of the author from _data/authors.yml
id: 1689484669 # Unique ID for the post, used for permalink
---
```
### Team Member pages
```yaml
---
# Team Member pages
member:
id: "member-id" # ID of the team member from _data/team.yml
name: "Member Name" # Name of the team member
---
```
### Special Class
`uj-signin-btn`: Automatically handles signin (just add `data-provider="google.com"` to the button)
`uj-signup-btn`: Automatically handles signup (just add `data-provider="google.com"` to the button)
`uj-language-dropdown`:
`uj-language-dropdown-item`
### Utility Classes
#### Max-Width Utilities
Ultimate Jekyll includes max-width utility classes based on Bootstrap's breakpoint sizes. These classes constrain an element's maximum width to match Bootstrap's standard responsive breakpoints:
- `.mw-sm` - Sets max-width to 576px
- `.mw-md` - Sets max-width to 768px
- `.mw-lg` - Sets max-width to 992px
- `.mw-xl` - Sets max-width to 1200px
- `.mw-xxl` - Sets max-width to 1400px
**Usage Examples:**
```html
```
These utilities are particularly useful for:
- Improving readability by preventing text from spanning too wide
- Creating consistent content widths across different sections
- Constraining forms, cards, and modals to reasonable sizes
- Maintaining design consistency with Bootstrap's grid system
### HTML Element Attributes
The `` element has data attributes for JavaScript/CSS targeting:
| Attribute | Values |
|-----------|--------|
| `data-theme-id` | Theme ID (e.g., `classy`) |
| `data-theme-target` | `frontend`, `backend`, `docs` |
| `data-bs-theme` | `light`, `dark` |
| `data-page-path` | Page permalink (e.g., `/about`) |
| `data-asset-path` | Custom asset path or empty |
| `data-environment` | `development`, `production` |
| `data-platform` | `windows`, `mac`, `linux`, `ios`, `android`, `chromeos`, `unknown` |
| `data-device` | `mobile` (<768px), `tablet` (768-1199px), `desktop` (>=1200px) |
| `data-runtime` | `web`, `extension`, `electron`, `node` |
| `aria-busy` | `true` (loading), `false` (ready) |
### Appearance Switching
Ultimate Jekyll supports dark/light/system theme switching with user preference persistence.
**JavaScript API:**
```javascript
webManager.uj().appearance.get(); // Returns 'dark', 'light', 'system', or null
webManager.uj().appearance.set('dark'); // Save and apply preference
webManager.uj().appearance.toggle(); // Toggle dark/light
webManager.uj().appearance.cycle(); // Cycle: dark → light → system
```
**HTML Dropdown Example:**
```html
{% uj_icon "sun" %}
{% uj_icon "moon-stars" %}
{% uj_icon "circle-half-stroke" %}
```
### Page Loading Protection System
Ultimate Jekyll includes an automatic protection system that prevents users from clicking buttons before JavaScript is fully loaded, eliminating race conditions and errors.
#### How It Works
1. Pages start with `data-page-loading="true"` on the HTML element
2. Certain buttons are automatically protected from clicks during this state
3. When JavaScript finishes loading, the attribute is removed and buttons become clickable
#### Protected Elements
During page load, these elements are automatically protected:
- All form buttons (``, ``, etc.)
- Elements with `.btn` class (Bootstrap buttons)
- Elements with `.btn-action` class (custom action triggers)
#### Using `.btn-action` Class
Add the `.btn-action` class to protect custom elements that trigger important actions:
```html
Save
About Us
Show Modal
```
**Use `.btn-action` for:** API calls, form submissions, data modifications, payments, destructive actions
**Don't use for:** Navigation, UI toggles, modals, accordions, harmless interactions
#### Form Protection Standards
All JS-managed forms use a layered protection strategy:
1. **`onsubmit="return false"`** on every `` managed by FormManager — prevents native submission before JS loads
2. **Button initial state** — buttons dependent on async data start `hidden` (revealed by `data-wm-bind`); auth buttons start `disabled` (enabled by FormManager's `ready()`)
3. **FormManager `autoReady`** — use `autoReady: false` when async work happens before form init, call `ready()` explicitly after
**Exception:** Traditional forms with an `action` attribute that intentionally navigate should NOT include `onsubmit="return false"`.
### Ad Units (Verts)
UJ provides ad unit includes that display Google AdSense ads with automatic fallback to in-house promo-server ads when AdSense is blocked or unfilled.
#### AdSense Include (with fallback)
```liquid
{% include /modules/adunits/adsense.html type="in-article" %}
{% include /modules/adunits/adsense.html type="display" vert-size="rectangle" %}
{% include /modules/adunits/adsense.html type="display" vert-size="300" %}
```
| Parameter | Default | Description |
|-----------|---------|-------------|
| `type` | `display` | Ad type: `display`, `in-article`, `in-feed`, `multiplex` |
| `vert-size` | (unconstrained) | Max height preset or pixel value |
| `slot` | From site config | Override the ad slot ID |
| `style` | `""` | Custom inline CSS |
#### Promo Server Include (direct, no AdSense)
```liquid
{% include /modules/adunits/promo-server.html vert-id="/verts/units/test/google" %}
{% include /modules/adunits/promo-server.html vert-id="/verts/units/test/google" vert-size="banner" %}
```
| Parameter | Default | Description |
|-----------|---------|-------------|
| `vert-id` | `""` | Path to the vert on promo-server |
| `vert-size` | (unconstrained) | Max height preset or pixel value |
| `style` | `""` | Custom inline CSS |
#### Size Presets
| Preset | Max Height | Typical Use |
|--------|-----------|-------------|
| `banner` | 150px | Horizontal banner ads |
| `leaderboard` | 90px | Wide horizontal ads |
| `rectangle` | 250px | Medium rectangle, in-content ads |
| `large-rectangle` | 600px | Large rectangle, sidebar ads |
| `skyscraper` | 600px | Tall sidebar ads |
Raw pixel values also accepted: `vert-size="300"` → 300px max-height. Omit `vert-size` for unconstrained rendering.
### Special Query Parameters
#### Authentication
* `authReturnUrl`: Redirects to this URL after authentication.
#### Testing Parameters
##### Account Page (`/account`)
* `_dev_subscription`: Override subscription data for testing billing states. The product ID is automatically patched to match a real product from the backend. Available values:
- `_dev_subscription=active`: Active paid subscription
- `_dev_subscription=trialing`: Free trial in progress
- `_dev_subscription=suspended`: Payment failed, access revoked
- `_dev_subscription=cancellation-requested`: Active but cancellation pending
- `_dev_subscription=cancelled`: Subscription ended
* `_dev_prefill=true`: Adds fake test data for development:
- Inserts fake referral data in the Referrals section
- Inserts fake session data in the Security section (active sessions)
##### Checkout Page (`/payment/checkout`)
* `_dev_brandId`: Override the brand ID for testing (e.g., `_dev_brandId=test-app`)
* `_dev_trialEligible`: Force trial eligibility status:
- `_dev_trialEligible=true`: User is eligible for trial
- `_dev_trialEligible=false`: User is not eligible for trial
* `_dev_cardProcessor`: Force a specific card payment processor (e.g., `_dev_cardProcessor=stripe` or `_dev_cardProcessor=chargebee`)
## JavaScript API
### Ultimate Jekyll Libraries
Ultimate Jekyll provides helper libraries in `src/assets/js/libs/` that can be imported as needed in your page modules.
#### Prerendered Icons Library
The prerendered icons library provides access to icons defined in page frontmatter. Icons are rendered server-side for optimal performance.
**Import:**
```javascript
import { getPrerenderedIcon } from '__main_assets__/js/libs/prerendered-icons.js';
```
**Function: `getPrerenderedIcon(iconName, classes)`**
A drop-in replacement for `uj_icon` in JavaScript contexts. The second argument works the same as `uj_icon`'s second argument.
**Parameters:**
- `iconName` (string) - Name of the icon to retrieve (matches `data-icon` attribute in frontmatter)
- `classes` (string, optional) - CSS classes for the `` wrapper (e.g. `"fa-md me-2"`). Without this, the icon has no size class.
**Returns:**
- (string) Icon HTML or empty string if not found
**Example:**
```javascript
import { getPrerenderedIcon } from '__main_assets__/js/libs/prerendered-icons.js';
// With size + classes (same as {% uj_icon "apple", "fa-xl" %})
$el.innerHTML = getPrerenderedIcon('apple', 'fa-xl');
// In a button (same as {% uj_icon "play", "fa-md me-1" %})
$btn.innerHTML = `${getPrerenderedIcon('play', 'fa-md me-1')} Run Now`;
// Without classes (no size class on the wrapper)
$el.innerHTML = getPrerenderedIcon('apple');
```
**Setup:**
Define icons in your page frontmatter (names only, no classes):
```yaml
---
prerender_icons:
- name: "apple"
- name: "android"
- name: "chrome"
---
```
**Available Icon Sizes (passed as second argument):**
- `fa-2xs` - Extra extra small
- `fa-xs` - Extra small
- `fa-sm` - Small
- `fa-md` - Medium (default base size)
- `fa-lg` - Large
- `fa-xl` - Extra large
- `fa-2xl` - 2x extra large
- `fa-3xl` - 3x extra large
- `fa-4xl` - 4x extra large
- `fa-5xl` - 5x extra large
Icons are automatically rendered in the page HTML and can be retrieved by importing the library function.
#### Authorized Fetch Library
The authorized fetch library simplifies authenticated API requests by automatically adding Firebase authentication tokens.
**Import:**
```javascript
import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';
```
**Function: `authorizedFetch(url, options)`**
**Parameters:**
- `url` (string) - The API endpoint URL
- `options` (Object) - Request options for wonderful-fetch (method, body, timeout, etc.)
**Returns:**
- (Promise) - The response from the API
**Example:**
```javascript
import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';
// Make an authenticated API call
const response = await authorizedFetch(serverApiURL, {
method: 'POST',
timeout: 30000,
response: 'json',
tries: 2,
body: {
command: 'user:get-data',
payload: { id: 'example' }
}
});
```
**How It Works:**
1. Retrieves the current Firebase user's ID token automatically
2. Adds the token to the request as an `Authorization: Bearer ` header
3. Makes the request using wonderful-fetch
4. Throws an error if no authenticated user is found
**Benefits:**
- No need to manually call `webManager.auth().getIdToken()`
- No need to add `authenticationToken` to request body
- Centralized authentication handling
- Consistent authentication across all API calls
#### FormManager Library
Lightweight form state management library with built-in validation, state machine, and event system.
**Import:**
```javascript
import { FormManager } from '__main_assets__/js/libs/form-manager.js';
```
**Basic Usage:**
```javascript
const formManager = new FormManager('#my-form', {
allowResubmit: true, // Allow form to be submitted again after success
resetOnSuccess: false, // Don't clear fields after success
warnOnUnsavedChanges: false // Don't warn on page leave
});
// Handle form submission
formManager.on('submit', async ({ data, $submitButton }) => {
const response = await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify(data)
});
if (!response.ok) {
throw new Error('Submission failed');
}
formManager.showSuccess('Form submitted successfully!');
});
```
**State Machine:**
```
initializing → ready ⇄ submitting → ready (or submitted)
```
**Events:**
| Event | Payload | Description |
|-------|---------|-------------|
| `submit` | `{ data, $submitButton }` | Form submission. Throw an error to show failure message. |
| `validation` | `{ data, setError }` | Custom validation before submit. Use `setError(fieldName, message)` to add errors. |
| `change` | `{ field, name, value, data }` | Called when any field value changes. |
| `statechange` | `{ state, previousState }` | Called when form state changes. |
| `honeypot` | `{ data }` | Called when honeypot is triggered (for spam tracking). |
**Validation:**
FormManager runs validation automatically before the `submit` event:
1. **HTML5 Validation** - Automatically checks `required`, `minlength`, `maxlength`, `min`, `max`, `pattern`, `type="email"`, `type="url"`
2. **Custom Validation** - Use the `validation` event for business logic
```javascript
formManager.on('validation', ({ data, setError }) => {
// Custom validation runs AFTER HTML5 validation
if (data.age && parseInt(data.age) < 18) {
setError('age', 'You must be 18 or older');
}
if (data.password !== data.confirmPassword) {
setError('confirmPassword', 'Passwords do not match');
}
});
```
Validation errors are displayed using Bootstrap's `is-invalid` class and `.invalid-feedback` elements. The first field with an error is automatically focused.
**Autofocus:**
When the form transitions to `ready` state, FormManager automatically focuses any field with the `autofocus` attribute:
```html
```
**Methods:**
| Method | Description |
|--------|-------------|
| `on(event, callback)` | Register event listener (chainable) |
| `ready()` | Manually transition to ready state (for `autoReady: false`) |
| `getData()` | Get form data as nested object |
| `setData(obj)` | Populate form from a nested object |
| `showSuccess(msg)` | Show success notification |
| `showError(msg)` | Show error notification |
| `reset()` | Reset form and state |
| `isDirty()` | Check if form has unsaved changes |
| `clearFieldErrors()` | Clear all validation error displays |
| `throwFieldErrors({ field: msg })` | Set errors and throw (for use in submit handler) |
**Nested Field Names (Dot Notation):**
Use dot notation in field `name` attributes for nested data structures:
```html
```
Produces:
```javascript
{
user: {
name: 'John',
address: {
city: 'NYC',
zip: '10001'
}
}
}
```
**Honeypot (Bot Detection):**
FormManager automatically rejects submissions if a honeypot field is filled. Fields matching `[data-honey]` or `[name="honey"]` are excluded from `getData()` and trigger rejection if filled.
```html
```
**Checkbox Handling:**
- **Single checkbox:** Returns `true` or `false`
- **Checkbox group (multiple with same name):** Returns object with each value as key
```html
```
**Multiple Submit Buttons:**
Access the clicked submit button to handle different actions:
```html
Save
Save as Draft
```
```javascript
formManager.on('submit', async ({ data, $submitButton }) => {
const action = $submitButton?.dataset?.action;
if (action === 'draft') {
await saveDraft(data);
formManager.showSuccess('Draft saved!');
} else {
await saveAndPublish(data);
formManager.showSuccess('Published!');
}
});
```
**Manual Ready Mode:**
For forms that need async initialization (e.g., loading data from API):
```javascript
const formManager = new FormManager('#my-form', { autoReady: false });
// Load data, then mark ready
const userData = await fetchUserData();
formManager.setData(userData);
formManager.ready(); // Now form is interactive
```
**Configuration Options:**
| Option | Default | Description |
|--------|---------|-------------|
| `autoReady` | `true` | Auto-transition to ready when DOM is ready |
| `initialState` | `'ready'` | State after autoReady fires |
| `allowResubmit` | `true` | Allow form resubmission after success |
| `resetOnSuccess` | `false` | Clear fields after successful submission |
| `warnOnUnsavedChanges` | `true` | Show browser warning when leaving with unsaved changes |
| `submittingText` | `'Processing...'` | Text shown on submit button during submission |
**Test Page:** Navigate to `/test/libraries/form-manager` to see FormManager in action with various configurations.
### ITM (Internal Tracking Medium)
Internal tracking system modeled after UTM for cross-property user journey tracking.
| Parameter | Purpose | Examples |
|-----------|---------|----------|
| `itm_source` | Platform/origin | `website`, `browser-extension`, `app`, `email` |
| `itm_medium` | Delivery mechanism | `modal`, `prompt`, `banner`, `tooltip` |
| `itm_campaign` | Specific campaign/feature | `exit-popup`, `premium-unlock`, `newsletter-signup` |
| `itm_content` | Specific context | Page path, feature ID, variant |
**Examples:**
```
# Website exit popup
?itm_source=website&itm_medium=modal&itm_campaign=exit-popup&itm_content=/pricing
# Extension premium unlock
?itm_source=browser-extension&itm_medium=prompt&itm_campaign=premium-unlock&itm_content=bulk-export
```
### Icons
* Fontawesome
* https://fontawesome.com/search
* Flags
* https://www.freepik.com/icon/england_4720360#fromView=resource_detail&position=1
* More
* Language
* https://www.freepik.com/icon/language_484531#fromView=family&page=1&position=0&uuid=651a2f0f-9023-4063-a495-af9a4ef72304
### Webpack Import Aliases
UJM defines two webpack aliases (in `src/gulp/tasks/webpack.js`) for importing assets in JavaScript:
| Alias | Resolves To | Purpose |
|-------|------------|---------|
| `__main_assets__` | `[UJM package]/dist/assets` | UJM's own built-in assets (core modules, libraries, pages) |
| `__project_assets__` | `[consuming project]/src/assets` | The consuming project's custom assets |
**`__main_assets__`** — Import UJM libraries and core modules:
```javascript
import { FormManager } from '__main_assets__/js/libs/form-manager.js';
import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';
import { getPrerenderedIcon } from '__main_assets__/js/libs/prerendered-icons.js';
```
**`__project_assets__`** — Import consuming project's own assets:
```javascript
// Used in src/index.js to load project-specific page modules
import(`__project_assets__/js/pages/${pageModulePath}`)
```
**How they work together:** `src/index.js` loads page modules from both aliases — first from `__main_assets__` (UJM defaults), then from `__project_assets__` (project overrides/extensions). If a project module doesn't exist, it gracefully skips. This enables a layered system where UJM provides defaults and consuming projects can extend or override page behavior.
**When to use which:**
- **`__main_assets__`** — When importing UJM-provided libraries, core modules, or referencing UJM's built-in page scripts
- **`__project_assets__`** — When a consuming project needs to import its own custom assets from within UJM-managed code
## Dev Flags
Add this to any js file to ONLY run in development mode (it will be excluded in production builds):
```
/* @dev-only:start */
{
// Your development-only code goes here
}
/* @dev-only:end */
```
> This project is "ultimate-jekyll", an NPM module that helps │
│ streamline development of Jekyll websites. A "consuming │
│ project" will require this NPM module to take advantage of │
│ its features like automatic folder structure setup, themes, │
│ and default pages to get a website up and running in │
│ seconds, while allowing further customization down the line. │
│ Right now i am struggling on the theme portion of this │
│ project. I want the user to be able to define the theme in │
│ their _config.yml (which currently they do by setting │
│ theme.id). I have some themes from the official bootstrap │
│ team. usually a theme comes with a frontend, a backend/admin │
│ dashboard, and docs. these 3 subparts of the theme have │
│ different html structure and css and js requirements. so i │
│ need a super easy system that allows me to make a file in │
│ the consuming project, say its the index.html for example, │
│ and i should easily be able to put which subseciton (or │
│ target as i call it) of the theme to use. so for an agency │
│ website i will probably use the frontend target, while for a │
│ chat app i will probably use the backend target. however, i │
│ need to be able to use