Open source @drobinetm/countries-flags

Implementation

Angular 17+
angular-countries-flags

Standalone component with ControlValueAccessor support for template-driven and reactive forms.

Try it live

Open the Angular demo page to try the component.

Open Angular demo

Styling examples

Go to the Angular styling section for Tailwind, Bootstrap, Angular Material, inline styles, and the slot map.

Open Angular styling

Installation

pnpm add @drobinetm/angular-countries-flags flag-icons
Copied

Load flag-icons in your angular.json styles array or import it in your global stylesheet:

/* styles.css or angular.json styles[] */
@import 'flag-icons/css/flag-icons.min.css';
Copied

Basic usage

Import the standalone component and use it:

// app.component.ts
import { Component } from '@angular/core'
import { CountriesFlagsComponent } from '@drobinetm/angular-countries-flags'
import { FormsModule } from '@angular/forms'

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CountriesFlagsComponent, FormsModule],
  template: `
    <drm-countries-flags
      [(ngModel)]="selectedCode"
      placeholder="Select a country"
    />
    <p>Selected: {{ selectedCode }}</p>
  `,
})
export class AppComponent {
  selectedCode: string | null = null
}
Copied

Reactive forms

The component implements ControlValueAccessor, so it works with reactive forms.

import { Component } from '@angular/core'
import { FormControl, ReactiveFormsModule } from '@angular/forms'
import { CountriesFlagsComponent } from '@drobinetm/angular-countries-flags'

@Component({
  standalone: true,
  imports: [CountriesFlagsComponent, ReactiveFormsModule],
  template: `
    <drm-countries-flags [formControl]="countryControl" />
  `,
})
export class MyComponent {
  countryControl = new FormControl<string | null>(null)
}
Copied

Filter & limit countries

<drm-countries-flags
  [(ngModel)]="code"
  [filter]="['es', 'us', 'br', 'ar', 'mx', 'co']"
  [max]="20"
  placeholder="Latin America + US + Spain"
/>
Copied

Change event

import { CountryChangeEvent } from '@drobinetm/angular-countries-flags'

<drm-countries-flags
  [(ngModel)]="code"
  (valueChange)="onCountryChange($event)"
/>

onCountryChange(event: CountryChangeEvent) {
  console.log(event.code)    // 'es'
  console.log(event.country) // { code: 'es', name: 'Spain' }
}
Copied

CSS integrations

In Angular, you can style the component with utility classes, CSS frameworks, or inline style objects. The examples below are Angular-specific.

Tailwind works well in Angular when you disable the bundled styles with unstyled.

<drm-countries-flags
  [(ngModel)]="selectedCode"
  [unstyled]="true"
  [classNames]="{
    root: 'w-full',
    trigger:
      'flex h-11 w-full items-center justify-between rounded-xl border border-slate-300 bg-white px-3 text-left text-slate-900 shadow-sm transition focus:outline-none focus:ring-2 focus:ring-sky-500',
    triggerContent: 'flex min-w-0 items-center gap-2',
    placeholder: 'text-slate-400',
    label: 'truncate',
    list: 'mt-2 max-h-64 overflow-auto rounded-xl border border-slate-200 bg-white p-1 shadow-2xl',
    option: 'flex cursor-pointer items-center gap-2 rounded-lg px-3 py-2 text-sm',
    optionActive: 'bg-slate-100',
    optionSelected: 'bg-sky-600 text-white',
    optionName: 'truncate'
  }"
  [styles]="{
    flag: { borderRadius: '9999px' }
  }"
></drm-countries-flags>
Copied

You can also use Bootstrap utilities through the exposed slot classes.

<drm-countries-flags
  [(ngModel)]="selectedCode"
  [unstyled]="true"
  [classNames]="{
    root: 'w-100',
    trigger: 'btn btn-outline-secondary w-100 d-flex align-items-center justify-content-between rounded-3 px-3 py-2',
    triggerContent: 'd-flex align-items-center gap-2 text-truncate',
    placeholder: 'text-secondary',
    list: 'mt-2 list-unstyled border rounded-3 bg-body shadow-lg p-1',
    option: 'd-flex align-items-center gap-2 rounded-3 px-3 py-2',
    optionActive: 'bg-light',
    optionSelected: 'bg-primary text-white',
    optionName: 'text-truncate'
  }"
  [styles]="{
    root: { minWidth: '280px' },
    flag: { borderRadius: '50%' }
  }"
></drm-countries-flags>
Copied

The component is not a native Angular Material form field, but you can match it to Material layouts and surfaces.

// app.component.ts
import { Component } from '@angular/core'
import { FormsModule } from '@angular/forms'
import { MatCardModule } from '@angular/material/card'
import { CountriesFlagsComponent } from '@drobinetm/angular-countries-flags'

@Component({
  standalone: true,
  imports: [FormsModule, MatCardModule, CountriesFlagsComponent],
  template: `
    <mat-card appearance="outlined">
      <mat-card-content>
        <drm-countries-flags
          [(ngModel)]="selectedCode"
          [unstyled]="true"
          [classNames]="{
            root: 'w-full',
            trigger: 'mat-mdc-button-base w-full d-flex align-items-center justify-content-between px-3 py-3 rounded-4 border',
            triggerContent: 'd-flex align-items-center gap-2 text-truncate',
            placeholder: 'mat-body-medium text-medium-emphasis',
            list: 'mt-2 rounded-4 border bg-surface p-1 mat-elevation-z8',
            option: 'd-flex align-items-center gap-2 rounded-3 px-3 py-2',
            optionActive: 'bg-light',
            optionSelected: 'bg-primary text-white'
          }"
          [styles]="{
            flag: { borderRadius: '9999px' }
          }"
        ></drm-countries-flags>
      </mat-card-content>
    </mat-card>
  `,
})
export class AppComponent {
  selectedCode: string | null = null
}
Copied

If your project computes styles in TypeScript, bind them with the styles input.

<drm-countries-flags
  [(ngModel)]="selectedCode"
  [unstyled]="true"
  [styles]="{
    root: { width: '100%', minWidth: '280px' },
    trigger: {
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'space-between',
      width: '100%',
      minHeight: '44px',
      padding: '0 14px',
      border: '1px solid #d4d4d8',
      borderRadius: '16px',
      backgroundColor: '#ffffff',
      color: '#111827'
    },
    triggerContent: { display: 'flex', alignItems: 'center', gap: '8px', minWidth: 0 },
    placeholder: { color: '#6b7280' },
    list: {
      marginTop: '8px',
      padding: '6px',
      listStyle: 'none',
      border: '1px solid #e5e7eb',
      borderRadius: '16px',
      backgroundColor: '#ffffff',
      boxShadow: '0 24px 48px rgba(15, 23, 42, 0.18)'
    },
    option: { display: 'flex', alignItems: 'center', gap: '8px', padding: '10px 12px', borderRadius: '12px' },
    optionActive: { backgroundColor: '#f3f4f6' },
    optionSelected: { backgroundColor: '#2563eb', color: '#ffffff' },
    flag: { borderRadius: '9999px' }
  }"
></drm-countries-flags>
Copied

Props / Inputs

InputTypeDefaultDescription
maxnumber0Max countries shown. 0 = unlimited.
filterstring[][]ISO alpha-2 whitelist. Empty = all.
placeholderstring"Select a country"Trigger placeholder text.
disabledbooleanfalseDisables the component.
unstyledbooleanfalseTurns off the bundled UI styles.
classNamesCountriesFlagsClassNames{}Classes by component slot.
stylesCountriesFlagsStyles{}Inline styles by component slot.
(valueChange)CountryChangeEventEmits { country, code }.