Angular starter-kit for building quality web apps fast. Now with Supabase.
- Click the button, create the repository, then clone it
- Create a copy of
.env.example
and name it.env
- Delete
./.github/FUNDING.yml
and./LICENSE
- Delete
./CHANGELOG.md
- a fresh one will be created on first release - Delete the
license
property in./package.json
and./package-lock.json
- Reset the
version
property in both./package.json
and./package-lock.json
to0.0.0
- Run
npm install
- this will also enable Husky - Run
ng serve
and start building!
Nice to do:
- Delete or update
./README.md
- Find and replace
https://jet-tau.vercel.app
with the base URL of your app - Empty the footer template
- Set
this._prefix
in StorageService andproject_id
in./supabase/config.toml
to something unique to the project
- Features
- Guides
- Sponsors
- License
- Modern Angular: Signals, new template syntax, Zoneless, Standalone, and more.
- Strict linting and formatting: Strict configurations for ESLint, Prettier and TypeScript.
- Performant: Modular and tree-shakeable. 80+ on PageSpeed Insights.
- Secure: Locked-down CSP and other security headers. 80+ on Mozilla Observatory.
- Always up-to-date: Actively maintained and regularly built from the ground-up. See 1000+ commits.
- @angular/material for components and theming support.
- @angular/service-worker for PWA support.
- @commitlint/* for semantic versioning.
- @jsverse/transloco for i18n.
- @ngx-env/builder for .env support.
- @supabase/supabase-js for Supabase integration.
- ga-gtag for Google Analytics integration.
- AppComponent
- FooterComponent
- HomePageComponent
- MessagePageComponent
- PageComponent
- ProfilePageComponent
- ResetPasswordPageComponent
- SettingsPageComponent
- SidenavComponent
- SignInPageComponent
- SignOutPageComponent
- SignUpPageComponent
- ToolbarComponent
- UpdatePasswordPageComponent
- AVATAR_FILE_MAX_SIZE
- COLOR_SCHEME_OPTIONS
- DEFAULT_COLOR_SCHEME_OPTION
- DEFAULT_LANGUAGE_OPTION
- DEFAULT_SETTINGS
- LANGUAGE_OPTIONS
- NAVIGATION_MENU_ITEMS
- LocalStorageKey
- QueryParam
- SessionStorageKey
- SupabaseBucket
- SupabaseEdgeFunction
- SupabaseRpcFunction
- SupabaseTable
- AlertService
- AnalyticsService
- LoggerService
- ProfileService
- ProgressBarService
- ServiceWorkerService
- SettingsService
- StorageService
- SupabaseService
- ToolbarTitleService
- UserService
If you need help with something not listed here, create a new issue.
Use ng g as you would in any other Angular project.
- Run
ng g c components/<component-name>
- In the
@Component()
decorator:- Set
changeDetection: ChangeDetectionStrategy.OnPush
- Set
imports: [TranslocoModule]
- Set
- In the class:
- Set
readonly #loggerService = inject(LoggerService);
- As a convention, at the end of the constructor, set
this.#loggerService.logComponentInitialization('<ClassName>');
- Set
- As a convention, add the component selector as a key in en.json and other translation files
- Update spec
In the template, wrap the contents in:
<ng-container *transloco="let t"> ... </ng-container>
- Run
ng g c components/<component-name>
- In the
@Component()
decorator:- Set
changeDetection: ChangeDetectionStrategy.OnPush
- Set
imports: [TranslocoModule, PageComponent]
- Set
- In the class:
- Set
readonly #loggerService = inject(LoggerService);
- As a convention, at the end of the constructor, set
this.#loggerService.logComponentInitialization('<ClassName>');
- Set
- As a convention, add the component selector as a key in en.json and other translation files
- Update spec
- Add a route to it in app.routes.ts
- Update sitemap-main.xml
- If required, add an icon and navigation link to it in NAVIGATION_MENU_ITEMS
In the template, wrap the contents in:
<ng-container *transloco="let t">
<jet-page
[seoDescription]="t('<component-selector>.seo.description')"
[seoKeywords]="t('<component-selector>.seo.keywords')"
[seoTitle]="t('<component-selector>.seo.title')"
[toolbarTitle]="t('<component-selector>.toolbar-title')"
>
...
</jet-page>
</ng-container>
- Run
ng g s services/<service-name>/<service-name>
- In the class:
- Set
readonly #loggerService = inject(LoggerService);
- As a convention, at the end of the constructor, set
this.#loggerService.logServiceInitialization('<ClassName>');
- Set
- Add mock
- Update spec
When enabled, Husky prevents pushing code that fails linting or building.
Run npm run format-staged
to format staged files. This runs automatically before every commit via Husky and Lint Staged.
Run npm run format
to format all files.
Run ng lint
. This runs automatically before every commit via Husky and ESLint.
Run ng test
.
Run npm run commit
, or commit directly with a valid commit message.
Commit messages that don't follow Conventional Commits will be blocked by Husky and Commitlint.
In .commitlintrc.json, update "scope-enum": [2, "always", ["general", "main", "<your-scope-1>", ..., "<your-scope-n>"]]
.
Update pre-commit. As a good practice, ensure every task is defined package.json and can be run independently.
More tasks mean longer commit times.
Run npm run reinstall-dependencies
. It runs the following subscripts to remove all dependencies, then install their latest versions: x:uninstall-devDependencies
, x:uninstall-dependencies
, x:install-dependencies
, x:install-devDependencies
. You shouldn't have to run these subscripts yourself.
For this to work, ensure the subscripts are updated every time a dependency is added or removed.
Jet uses Material Symbols for icons. Instead of downloading the entire font, each icon is explicitly specified in index.html. To add or remove icons, update the icon names alphabetically in the <link>
element (read more about this requirement here).
Custom SVG icons can be loaded in _setIcons()
in AppComponent.
- Generate a Personal Access Token token with no expiry
- Save it to Actions > Secrets as
RELEASE_PLEASE_TOKEN
npx supabase status
npx supabase start
npx supabase stop
npx supabase functions new <function-name>
npx supabase functions serve
npx supabase migrations new
npx supabase db reset
- Set
project_id
in config.toml to something unique to the project
Support development of Jet. Pay what you like.
This project is licensed under the MIT License. See LICENSE for details.