diff --git a/app/components/button.hbs b/app/components/button.hbs
new file mode 100644
index 0000000..c9631ca
--- /dev/null
+++ b/app/components/button.hbs
@@ -0,0 +1,14 @@
+
diff --git a/app/components/button.js b/app/components/button.js
new file mode 100644
index 0000000..6529436
--- /dev/null
+++ b/app/components/button.js
@@ -0,0 +1,83 @@
+import Component from '@glimmer/component';
+import { action } from '@ember/object';
+import { tracked } from '@glimmer/tracking';
+
+export default class ButtonComponent extends Component {
+ @tracked _state = 'default';
+
+ get disabled() {
+ if (this.args.disabled !== undefined) {
+ return this.args.disabled;
+ }
+
+ return this.isPending;
+ }
+
+ get type() {
+ return this.args.type ?? 'button';
+ }
+
+ get icon() {
+ if (this.isPending && this.args.pendingIcon) {
+ return this.args.pendingIcon;
+ } else if (this.isFulfilled && this.args.fulfilledIcon) {
+ return this.args.fulfilledIcon;
+ } else if (this.isRejected && this.args.rejectedIcon) {
+ return this.args.rejectedIcon;
+ } else if (this.isSettled && this.args.settledIcon) {
+ return this.args.settledIcon;
+ }
+
+ return this.args.icon ?? null;
+ }
+
+ get state() {
+ return this.args.state ?? this._state;
+ }
+ set state(state) {
+ this._state = state;
+ }
+
+ get isPending() {
+ return this.state === 'pending';
+ }
+
+ get isFulfilled() {
+ return this.state === 'fulfilled';
+ }
+
+ get isRejected() {
+ return this.state === 'rejected';
+ }
+
+ get isSettled() {
+ return this.isFulfilled || this.isRejected;
+ }
+
+ get text() {
+ return this.args[`${this.state}Text`] || this.args.text;
+ }
+
+ @action
+ async handleClick() {
+ if (typeof this.args.onClick !== 'function') {
+ return;
+ }
+
+ this.state = 'pending';
+
+ try {
+ await this.args.onClick();
+
+ if (!this.isDestroyed) {
+ this.state = 'fulfilled';
+ }
+ } catch (error) {
+ if (!this.isDestroyed) {
+ this.state = 'rejected';
+ }
+
+ throw error;
+ }
+ }
+}
diff --git a/tests/integration/components/button-test.js b/tests/integration/components/button-test.js
new file mode 100644
index 0000000..a17185a
--- /dev/null
+++ b/tests/integration/components/button-test.js
@@ -0,0 +1,26 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+
+module('Integration | Component | button', function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('it renders', async function (assert) {
+ // Set any properties with this.set('myProperty', 'value');
+ // Handle any actions with this.set('myAction', function(val) { ... });
+
+ await render(hbs``);
+
+ assert.dom(this.element).hasText('');
+
+ // Template block usage:
+ await render(hbs`
+
+ `);
+
+ assert.dom(this.element).hasText('template block text');
+ });
+});