See how Stencil fits into the entire Ionic Ecosystem ->
Stencil is part of the Ionic Ecosystem ->

Stencil Style Guide

This is a component style guide created and enforced internally by the core team of Stencil, for the purpose of standardizing Stencil components. This should only be used as a reference for other teams in creating their own style guides. Feel free to modify to your team's own preference.

In order to enforce this (or your team's) style guide, we recommend leveraging a static analysis tool like ESLint. @stencil/eslint-plugin provides rules specifically for writing Stencil components.

This guide once recommended TSLint as a static analysis tool. TSLint has been deprecated by its maintaining organization in favor of ESLint and is no longer recommended by the Stencil team.


  • ファイルごとに1つのコンポーネント。
  • ディレクトリごとに1つのコンポーネント。 類似のコンポーネントを同じディレクトリにグループ化することは理にかなっているかもしれませんが、各コンポーネントに独自のディレクトリがあると、コンポーネントを文書化する方が簡単であることがわかりました。
  • コンポーネントの実装(.tsx)とスタイルは同じディレクトリに存在する必要があります。

Ionic Coreの例:

├── my-card
│   ├── my-card.ios.css
│   ├──
│   ├── my-card.css
│   ├── my-card.tsx
│   └── test
│       └── basic
│           ├── e2e.js
│           └── index.html
├── my-card-content
│   ├── my-card-content.ios.css
│   ├──
│   ├── my-card-content.css
│   └── my-card-content.tsx
├── my-card-title
│   ├── my-card-title.ios.css
│   ├──
│   ├── my-card-title.css




The prefix has a major role when you are creating a collection of components intended to be used across different projects, like @ionic/core. Web Components are not scoped because they are globally declared within the webpage, which means a "unique" prefix is needed to prevent collisions. The prefix also helps to quickly identify the collection a component is part of. Additionally, web components are required to contain a "-" dash within the tag name, so using the first section to namespace your components is a natural fit.




代わりに、独自の名前またはブランドを使用してください。 たとえば、Ionicコンポーネントには、すべて「ion-」というプレフィックスが付いています。



コンポーネントはアクションではなく、概念的には「モノ」です。 「animating」の代わりに「animation」など、動詞の代わりに名詞を使用することをお勧めします。 「入力」、「タブ」、「ナビゲーション」、「メニュー」はいくつかの例です。





クラスはスコープされているため、コンポーネントのES6クラスの名前にはプレフィックスを付けないでください。 衝突の危険はありません。

  tag: 'ion-button'
export class Button { ... }

  tag: 'ion-menu'
export class Menu { ... }


  1. Variable decorators should be inlined.
@Prop() name: string;
@Element() el: HTMLElement;
  1. Method decorator should be multi-line
onClick() {
  1. Use private variables and methods as much possible: They are useful to detect dead code and enforce encapsulation. Note that this is a feature which TypeScript provides to help harden your code, but using private, public or protected does not make a difference in the actual JavaScript output.

  2. Code with Method/Prop/Event/Component decorators should have JSDocs: This allows for documentation generation and for better user experience in an editor that has TypeScript intellisense


ロバートC.マーチンの クリーンコード からのNewspaperのメタファー



  tag: 'ion-something',
  styleUrls: {
    ios: 'something.ios.css',
    md: '',
    wp: 'something.wp.css'
export class Something {

   * 1. Own Properties
   * Always set the type if a default value has not
   * been set. If a default value is being set, then type
   * is already inferred. List the own properties in
   * alphabetical order. Note that because these properties
   * do not have the @Prop() decorator, they will not be exposed
   * publicly on the host element, but only used internally.
  num: number;
  someText = 'default';

   * 2. Reference to host HTML element.
   * Inlined decorator
  @Element() el: HTMLElement;

   * 3. State() variables
   * Inlined decorator, alphabetical order.
  @State() isValidated: boolean;
  @State() status = 0;

   * 4. Public Property API
   * Inlined decorator, alphabetical order. These are
   * different than "own properties" in that public props
   * are exposed as properties and attributes on the host element.
   * Requires JSDocs for public API documentation.
  @Prop() content: string;
  @Prop() enabled: boolean;
  @Prop() menuId: string;
  @Prop() type = 'overlay';

   * Prop lifecycle events SHOULD go just behind the Prop they listen to.
   * This makes sense since both statements are strongly connected.
   * - If renaming the instance variable name you must also update the name in @Watch()
   * - Code is easier to follow and maintain.
  @Prop() swipeEnabled = true;

  swipeEnabledChanged(newSwipeEnabled: boolean, oldSwipeEnabled: boolean) {

   * 5. Events section
   * Inlined decorator, alphabetical order.
   * Requires JSDocs for public API documentation.
  @Event() ionClose: EventEmitter;
  @Event() ionDrag: EventEmitter;
  @Event() ionOpen: EventEmitter;

   * 6. Component lifecycle events
   * Ordered by their natural call order, for example
   * WillLoad should go before DidLoad.
  connectedCallback() {}
  disconnectedCallback() {}
  componentWillLoad() {}
  componentDidLoad() {}
  componentWillUpdate() {}
  componentDidUpdate() {}
  componentWillRender() {}
  componentShouldRender(newVal: any, oldVal: any, propName: string) {}
  componentDidRender() {}

   * 7. Listeners
   * It is ok to place them in a different location
   * if makes more sense in the context. Recommend
   * starting a listener method with "on".
   * Always use two lines.
  @Listen('click', { enabled: false })
  onClick(ev: UIEvent) {

   * 8. Public methods API
   * These methods are exposed on the host element.
   * Always use two lines.
   * Public Methods must be async.
   * Requires JSDocs for public API documentation.
  async open(): Promise<boolean> {
    // ...
    return true;

  async close(): Promise<void> {
    // ...

   * 9. Local methods
   * Internal business logic. These methods cannot be
   * called from the host element.
  prepareAnimation(): Promise<void> {
    // ...

  updateState() {
    // ...

   * 10. render() function
   * Always the last public method in the class.
   * If private methods present, they are below public methods.
  render() {
    return (
        side={this.isRightSide ? 'right' : 'left'}
          'something-is-animating': this.isAnimating
        <div class='menu-inner page-inner'>