❌

Normal view

Apple apresenta o iPhone Duo, seu primeiro dobrável

9 September 2026 at 20:11

Depois de anos de especulações, a Apple enfim revelou o iPhone Duo, a sua estreia oficial no segmento dos smartphones com tela dobrável.

O formato já é conhecido por quem acompanhou os rumores e viu as mudanças recentes entre os dobráveis de outras empresas, como a HUAWEI e a Samsung: o aparelho conta com uma tela externa (Ceramic Shield 2) de 5,4 polegadas e uma tela interna de 7,6″, seguindo um formato parecido com o de um passaporte. Ambas também têm exatamente a mesma proporção, e o acabamento fosco (nano-texture) está presente na tela interna.

Divulgação/Apple

Quando aberto, o iPhone Duo torna-se o iPhone mais fino já vendido pela Apple, com 5,2mm. O mecanismo de dobradiça também foi outro ponto particularmente trabalhado pela companhia (e o possível motivo de tanta demora para esse lançamento), sendo composto por mais de 100 componentes — a tampa da dobradiça, por sua vez, é impressa em 3D.

Divulgação/Apple

Outro elemento que vale ser mencionado é a volta do Touch ID nos iPhones, já que a finura do aparelho não comporta os sensores do Face ID. A biometria digital está localizada na lateral direita do aparelho.

Também estão agora nessa lateral os principais controles em nível de software do iOS 27, incluindo a barra de status (que ganhou um novo design mais compacto), as barras de ferramentas dos apps e até mesmo o Dock, que agora fica na vertical. Todos os principais apps do sistema foram adaptados para o novo formato de modo a aproveitar o maior espaço disponível, além de permitirem que você use o smartphone com ele parcialmente fechado.

Divulgação/Apple
Divulgação/Apple

O modo Em Espera (Stand By) é compatível com ambos os displays, transformando o iPhone Duo em um verdadeiro porta-retrato interativo ou em um relógio digital, só para citar alguns exemplos. A Apple também introduziu novos mostradores para esse recurso focados nos apps Calendário (Calendar) e Tempo (Weather).

Divulgação/Apple
Divulgação/Apple

Assim como no iPad, é possível colocar dois apps no modo Split View ao usar a tela interna e, em breve (provavelmente depois de um update), usar uma Apple Pencil (USB-C) para fazer ilustrações e anotações em apps suportados.

Divulgação/Apple

O iPhone Duo vem equipado com o A20 Pro, chip com uma litografia de 2 nanômetros, possuindo 6 núcleos destinados a CPU 1Central processing unit, ou unidade central de processamento. (2 de desempenho e 4 de eficiência) e uma GPU 2Graphics processing unit, ou unidade de processamento gráfico. de 7 núcleos, além de um Display Engine novo para “empurrar” as duas telas. Com tudo isso, a Apple promete uma performance sustentada até 35% maior em comparação com o iPhone 17 Pro.

O novíssimo modem C2 também está presente no primeiro dobrável da Apple, oferecendo conexões até 50% mais rápidas e conectividade 5G mmWave (apenas nos Estados Unidos). Ele também está mais eficiente do ponto de vista energético, consumindo 15% menos energia em comparação com o modem C1X. Responsável por possibilidade conexões Wi-Fi 7, Bluetooth 6 e Thread, o chip N1 também deu as caras nesse smartphone.

O dobrável conta com duas câmeras frontais, uma para cada tela do aparelho; mais tradicional, a câmera frontal da tela externa (Center Stage) fica no canto superior direito do dispositivo, servindo também como uma Ilha Dinâmica (Dynamic Island), enquanto a câmera frontal da tela interna (FaceTime camera) está um pouco mais ao centro e, vejam só, fica posicionada sob o display, aparecendo apenas quando acionada — seja para capturar fotos/vídeos ou para exibir Atividades ao Vivo (Live Activities).

Divulgação/Apple

Já no conjunto de câmeras traseiras, localizado no módulo de câmera horizontal em formato de pílula, temos a câmera Fusion principal de 48 megapixels — a qual conta com shutter lag zero e também pode simular uma lente telefoto de 2x — e uma lente Fusion ultra-angular de também 48MP — mesmíssima lente dos iPhones 18 Pro. E, por conta da tela externa, usuários podem tirar selfies facilmente com essas lentes, que também são capazes de capturar fotos automaticamente graças ao recurso Smart Take, que detecta poses.

Divulgação/Apple

Além de novas formas de navegar pelo sistema, as duas telas também desbloqueiam maneiras inéditas de capturar imagens. De acordo com a Apple, a FaceTime camera permite realizar chamadas de vídeo com o iPhone Duo completamente aberto ou com ele parcialmente fechado — enquanto fica apoiado numa mesa, por exemplo. O iPhone Duo também pode exibir a imagem de uma pessoa durante uma chamada de vídeo na tela externa, permitindo que outras pessoas participem da conversa sem precisar compartilhar uma câmera com o dono do aparelho.

Mesmo com uma espessura reduzida, teremos uma das maiores baterias já utilizadas pela Apple em um iPhone, capaz de reproduzir até 31 horas ininterruptas de vídeo com a tela interna e até 44 horas com o display de fora. Em uso alternado, o iPhone Duo pode durar até 24 horas com uma única carga, de modo que apenas 15 minutos na tomada (via cabo) são suficientes para garantir 50% de carga. Carregadores MagSafe e Qi2 também são suportados.

Em termos de durabilidade, o iPhone Duo vem com a certificação IP68 contra água e poeira, além de contar, como dito, com a tecnologia Ceramic Shield 2 na tela externa. A primeira geração do Ceramic Shield também está presente nesse aparelho na forma do vidro traseiro.

Preço e disponibilidade

O iPhone Duo contará com opções de 256GB, 512GB, 1TB e 2TB de armazenamento, com o valor inicial de US$2.000 nos Estados Unidos e podendo chegar a US$3.200. Ele pode ser adquirido nas cores branco-estrela (star white) e céu noturno (night sky).

No Brasil e em Portugal, o dobrável sairá pelos preços iniciais de R$22.000 e 2.400€, respectivamente.

Nos EUA, no Brasil e em Portugal, a pré-venda começará no dia 16 de outubro (uma sexta-feira). O lançamento geral ocorrerá no dia 23/10 (outra sexta) em todas essas localidades.


Apoio: Google Workspace

Logo do Google Workspace

Junte-se a milhões de empresas que usam as versões premium de apps como Gmail, Agenda, Drive e Meet. Com o Gemini integrado ao Google Workspace, você escreve, resume e organiza em segundos — e tudo continua funcionando de qualquer lugar, com apps completos e intuitivos para iPhone, iPad e Mac. Comece hoje mesmo com 20% de desconto nos 6 primeiros meses!

NOTA DE TRANSPARÊNCIA: O MacMagazine recebe uma pequena comissão sobre vendas concluídas por meio de links deste post, mas você, como consumidor, não paga nada mais pelos produtos/serviços usando os nossos links de afiliado.

Notas de rodapé

  • 1
    Central processing unit, ou unidade central de processamento.
  • 2
    Graphics processing unit, ou unidade de processamento gráfico.

Samsung anuncia Galaxy Z Fold8, Z Fold8 Ultra e Z Flip8, além de novos smartwatches

22 July 2026 at 17:51

Na mais recente edição do seu tradicional evento anual Unpacked, a Samsung revelou os seus novos smartphones dobráveis (o Galaxy Z Fold8, o Galaxy Z Fold8 Ultra e o Galaxy Z Flip8), além de novos smartwatches.

Os novos dobráveis chegam meses antes de a Apple alegadamente adentrar nesse segmento do mercado com o famigerado “iPhone Ultra” — logo, as apostas da gigante sul-coreana baterão de frente com o futuro dispositivo da Maçã.

Sem mais delongas, vamos conferir os detalhes de cada anúncio abaixo!

Galaxy Z Fold8

Samsung Galaxy Z Fold8
Divulgação/Samsung

Agora que os dobráveis da Samsung possuem três variantes, o Z Fold tradicional passou por um reposicionamento, tornando-se talvez a adição mais diferente dentre toda a linha de novos produtos anunciados.

Seu grande destaque é a reformulação no design, adotando um formato que se convencionou chamar de “passaporte”, com uma proporção maior do que aquilo que foi estabelecido nas últimas gerações do dobrável principal da Samsung. Isso resultou em uma tela externa de 5,5 polegadas e aspect ratio de 10:16, além de uma principal de 7,6″ e proporção de 4:3 — especificações bem semelhantes ao que se especula sobre o “iPhone Ultra” —, ambas com taxas de atualização de até 120Hz.

A sul-coreana destacou ainda o peso do modelo, sendo o celular dobrável mais leve já lançado por ela até agora (201 gramas, para ser exato).

Uma das principais diferenças do modelo em relação ao Z Fold8 Ultra está nas câmeras: o modelo padrão conta apenas com duas lentes traseiras (um sensor principal e uma lente ultra-angular, ambos com 50 megapixels), além dos 200mAh de diferença na bateria (4.800mAh no caso do Z Fold8 e 5.000mAh no caso do Z Fold8 Ultra). A linha também marca a estreia das baterias de silício-carbono nos celulares da fabricante.

Quanto ao restante das suas especificações técnicas, destaca-se pela implementação da tecnologia Flex Titanium — baseada em alumínio — para a resistência da tela, a presença do chip Snapdragon 8 Elite Gen 5, opções a partir de 12GB de memória e 256GB de armazenamento (até 1TB), resistência IP48 contra água e poeira, além de suporte ao carregamento de 45W com fio e 15W sem fio. Assim como os demais aparelhos, ele chega com o Android 17 e a One UI 9.

Nos Estados Unidos, ele chega a partir de US$1.900 (US$200 a menos que o Z Fold8 Ultra e US$100 a menos que o Z Fold7) nas cores preta, branca, lavanda e verde pistache (esta última exclusiva para compra no site oficial da Samsung).

Galaxy Z Fold8 Ultra

Divulgação/Samsung

Para os entusiastas do tradicional formato dos dobráveis da Samsung, a melhor opção é o modelo Ultra. Ele conta com as mesmas 8″ na tela principal e 6,5″ na tela externa do Z Fold7, com aspect ratios de 3:2 e 21:9, respectivamente.

Outra diferença notável em relação ao modelo menor é a presença da lente teleobjetiva de 10MP, aos moldes da presente no Z Fold7.

Como citado anteriormente, o aparelho conta com uma bateria de 5.000mAh de silício-carbono, além de compartilhar o Snapdragon 8 Elite Gen 5, as mesmas opções de memória/armazenamento, a resistência IP48 e o suporte aos 45W de carregamento com fio e 15W sem fio do Z Fold8.

Um de seus maiores destaques (nesse caso, negativo) é o preço: US$2.100 — um aumento em comparação ao modelo equivalente da geração passada (vale lembrar, o Z Fold7 chegou por US$2.000 lá fora). Ele está disponível nas cores preta, branca, roxo escuro e verde escuro (esta última exclusiva para compra no site oficial da Samsung).

Galaxy Z Flip8

Samsung Galaxy Z Flip8
Divulgação/Samsung

Modelo mais básico da nova linha, o Z Flip8 se destaca pela redução na espessura (13,1mm dobrado contra os 13,7mm do antecessor) e no peso (188g contra 180g do Z Flip7), possibilitando o que a Samsung chama de “uma experiência cotidiana sem vinco”.

Ele conta com uma tela interna de 6,9″, enquanto a externa tem 4,1″, com proporções de 22:9 e 1:1, respectivamente, e suporte a taxas de atualização de 120Hz em ambas. O aparelho chega com um conjunto duplo de câmeras, sendo a lente principal de 50MP, um sensor ultra-angular de 12MP e câmera frontal de 10MP.

Ele chega com o processador Exynos 2600, resistência IP48, 12GB de memória e opções de armazenamento de 256GB e 512GB. Em termos de bateria e recarga, o componente de silício-carbono possui uma capacidade de 4.300mAh, carregamento de até 25W por fio e 15W sem fio.

O Z Flip8 custa US$1.200 (um aumento de US$100 em relação ao Z Flip7) e está disponível nas cores azul, preta, vermelha coral e menta (esta última exclusiva para compra no site oficial da Samsung).

Galaxy Watch9 e Ultra2

Ambos os modelos ganharam suporte ao recurso “levante para falar”, que aciona de imediato o Gemini do Google sem a necessidade de toque ou botão. Os dois relógios contam com o Wear OS 7 e a One UI 9 Watch, compatíveis com smartphones que possuam Android 13 ou superior.

Samsung Galaxy Watch9 e Ultra2
Divulgação/Samsung

Com especificações e design um tanto semelhantes ao seu antecessor, as grandes novidades do Galaxy Watch9 incluem a ampliação de bateria (o modelo de 40mm ganhou um aumento de quase 20%, com 390mAh, enquanto a versão de 44mm possui 445hmAh), a pulseira com design Air Pocket (até 25% mais leve que a presente no Watch8) e a estreia do processador Snapdragon Wear Elite.

Ele continua com as opções de 2GB de memória e 32GB de armazenamento e está disponível em caixas de 40mm e 44mm, nas cores preto e creme, custando a partir de US$380 (um aumento de US$30 em relação ao Watch8).

Já o Galaxy Watch Ultra2 adota uma reformulação na identidade visual que o aproxima do restante da linha, sendo até 12% mais fino que o seu antecessor, mantendo a bateria de 800mAh. A tela também ganhou melhorias, contando com 5.000 nits de brilho máximo.

Para experiências aquáticas, o Ultra2 atende o padrão EN13319 para mergulho e possui certificação IP69K. Além disso, o app Marz deverá receber suporte à metrificação de velocidades e orientações de descompressão.

Disponível nas cores titânio preto e titânio prata, o modelo está disponível a partir de US$700, um aumento de US$50 em relação à primeira geração.

Inteligência artificial

Durante o evento, a Samsung reforçou sua ciência e presença quanto às IAs agênticas, expandindo a experiência para além das telas dos smartphones e passando a oferecer assistência contínua de IA em tempo real nos dispositivos do seu ecossistema; o foco da Samsung é a entrega da experiência hands-free (ou seja, mãos livres). Ela afirmou ainda que essa integração contará com mecanismos de proteção e segurança, embora não tenha adentrado em detalhes técnicos de como fará isso.

A empresa também mencionou a integração de dados biométricos coletados pelos Galaxy Watch ao fluxo de trabalho médico, focada na prevenção de riscos de saúde com o auxílio da inteligência artificial (IA), permitindo a sugestão de refeições e treinos adaptados com base em interpretações de biossinais.

Preço e disponibilidade

Os novos smartphones chegarão ao Brasil no dia 6 de agosto, mas ainda não tiveram seus preços divulgados; ainda assim, a Samsung já antecipou que a aquisição de algum dos dispositivos durante a pré-venda garantirá até seis meses de Google AI Pro sem custos adicionais.

NOTA DE TRANSPARÊNCIA: O MacMagazine recebe uma pequena comissão sobre vendas concluídas por meio de links deste post, mas você, como consumidor, não paga nada mais pelos produtos/serviços usando os nossos links de afiliado.

Angular Signals in practice: Building a signal-first form in Angular

18 June 2026 at 10:00

Understanding a reactivity model in the abstract is useful, but it is ultimately incomplete without seeing how it shapes real application code. Concepts such as state, derivation, and explicit dependencies only become meaningful when they influence how forms are built, validated, and maintained in practice.

In two previous articles, “Angular Signal Forms: From event pipelines to signal-driven state” and “Angular Signals explained: How pull-based reactivity changes how we model state,” we reframed form behavior as a state-driven problem and examined Angular Signals as a pull-based reactivity model well-suited to that kind of work. The natural next step is to apply those ideas to an actual Angular form and observe how the architecture changes when state becomes the primary concern.

This article focuses on a concrete example: a modest but realistic registration form. Rather than introducing new concepts, the goal here is to make earlier ideas tangible. We will see how a signal-backed model reshapes validation, interaction state, and submission logic, and how much coordination logic simply disappears when form behavior is expressed declaratively.

The focus here is not on novelty or completeness, but on making the underlying ideas easier to reason about. By walking through a signal-first form from model definition to submission, we can evaluate whether this approach truly reduces complexity and where it introduces new trade-offs that teams should understand before adopting it more broadly.

Read the series:

Implementing a signal-first registration form

With the conceptual groundwork in place, we can now turn theory into a concrete implementation. In this section, we will build a fully working registration form using Angular’s Signal Forms API. This example is deliberately modest in scope, but it is designed to serve as the foundation for the rest of the series. Each subsequent article will extend this same example rather than introducing a new one.

The form collects an email address, a password, a confirmation password, and explicit acceptance of terms. While simple on the surface, this structure allows us to explore field-level validation, cross-field constraints, interaction state, and submission behavior, all without reverting to event-driven form logic.

Project setup and structure

The example assumes a standard Angular application created with the Angular CLI and configured to use Signals (Angular 17+). The Signal Forms APIs (Angular 21+) live under @angular/forms/signals, which must be explicitly imported.

https://github.com/sonukapoor/angular-signal-forms

The folder structure is intentionally conservative:

src/
  app/
    registration/
      registration.component.ts
      registration.component.html
      registration.model.ts

Separating the model from the component keeps form state independent of presentation. This becomes increasingly valuable as the form grows or is reused across multiple components.

Defining the form model

We begin by defining the shape of the data that the form collects. This is a plain TypeScript interface with no Angular dependencies. Treating the form model as a simple data structure reinforces the idea that the form’s values are just state.

// registration.model.ts
export interface RegistrationData {
  email: string;
  password: string;
  confirmPassword: string;
  acceptedTerms: boolean;
}

This interface mirrors what would typically be sent to a back-end API. There is no duplication of state, no separate “form value” object, and no mapping required at submission time.

Creating the signal-backed form

The form itself is created in the component using a writable signal as the source of truth. The form() function attaches form semantics validation, field state, and submission to that signal.

// registration.component.ts
import { CommonModule } from "@angular/common";
import { Component, signal } from "@angular/core";
import {
  email,
  form,
  FormField,
  required,
  submit,
} from "@angular/forms/signals";
import { RegistrationData } from "./registration.model";

@Component({
  selector: "app-registration",
  imports: [FormField, CommonModule],
  templateUrl: "./registration.html",
  styleUrl: "./registration.css",
})
export class Registration {
  readonly model = signal({
    email: "",
    password: "",
    confirmPassword: "",
    acceptedTerms: false,
  });

  readonly registrationForm = form(this.model, (schema) => {
    required(schema.email, { message: "Email is required" });
    email(schema.email, { message: "Enter a valid email address" });

    required(schema.password, { message: "Password is required" });
    required(schema.confirmPassword, {
      message: "Please confirm your password",
    });

    required(schema.acceptedTerms, {
      message: "You must accept the terms to continue",
    });
  });

  async onSubmit(event?: Event) {
    event?.preventDefault();

    await submit(this.registrationForm, (value) => {
      console.log(value());
      // Mock Server Call
      return Promise.resolve([
        {
          kind: "EmailAlreadyExists",
          field: this.registrationForm.email,
          error: { kind: "server", message: "Email already taken" },
        },
      ]);
    });
  }
}

Several design decisions are worth noting.

First, the model signal is defined as read-only. All mutations to the model occur through form bindings, not ad hoc assignments in the component. This keeps the component declarative and avoids the temptation to manipulate form state imperatively.

Second, validation is declared in one place. The schema function describes constraints on the model without introducing control trees, validator arrays, or observable pipelines. Angular takes responsibility for re-running validation whenever the model changes.

Finally, submission logic is explicit. The submit() helper ensures that the form is valid before invoking the callback, and it passes the current model value directly. There is no need to check flags or manually extract values.

Binding the form to the template

With the form defined, the next step is to bind it to the template. Signal Forms provide the [formField] directive, which connects an input element directly to a field in the form schema.





  
@if (registrationForm().errors().length > 0) {
@for (error of registrationForm().errors(); track error.message) {

{{ error.kind }}

}
}
@if ( registrationForm.acceptedTerms().invalid() && registrationForm.acceptedTerms().touched() ) {

{{ registrationForm.acceptedTerms().errors()[0].message }}

}
@if ( registrationForm.confirmPassword().invalid() && registrationForm.confirmPassword().touched() ) {

{{ registrationForm.confirmPassword().errors()[0].message }}

}
@if ( registrationForm.password().invalid() && registrationForm.password().touched() ) {

{{ registrationForm.password().errors()[0].message }}

}
@if ( registrationForm.email().invalid() && registrationForm.email().touched() ) {

{{ registrationForm.email().errors()[0].message }}

}

What stands out here is the absence of indirection. Each input binds directly to a field. Validation state is accessed through signals such as invalid() and touched(). Error messages are read from a structured error object, not reconstructed manually.

This template contains no subscriptions, no async pipes, and no event handlers for value changes. The UI simply reflects the current form state.

Interaction state and user experience

One of the common criticisms of declarative form models is that they obscure user interaction logic. Signal Forms address this directly by exposing interaction metadata as signals.

The touched() signal determines whether a field has been interacted with. By combining it with invalid(), we control when validation messages appear. This logic remains purely declarative: the template describes when errors should be visible, and Angular ensures the signals stay up-to-date.

The disabled state of the submit button is derived from registrationForm.invalid(). There is no need to manually enable or disable it in response to events. If the form becomes valid, the button is enabled automatically.

Why this scales

Even at this early stage, several advantages of a signal-first form model are apparent. The form’s behavior is expressed in terms of state and derivation, not events. The model, validation rules, and UI bindings are clearly separated. There is no duplication of logic between the component and the template.

As the form grows, this structure holds. Additional fields introduce additional schema entries and template bindings, not new subscription logic. Cross-field validation can be added declaratively. Asynchronous validation and persistence can be layered on without rewriting the core model.

Most importantly, the form remains inspectable. At any point during execution, the model signal reflects the current state of the form. Derived state validity, errors, and UI flags can be understood by reading the code, not by tracing runtime behavior.

What we did not solve yet (and why)

At this stage, it would be easy to walk away with the impression that Signal Forms eliminates most of the hard problems associated with form handling. That impression would be misleading. What we have built so far is intentionally incomplete, not because the approach falls short, but because introducing too much too early obscures the value of the underlying model.

One area we have deliberately postponed is cross-field validation that expresses richer business rules. Many real-world forms depend on relationships between fields rather than isolated constraints. Password confirmation is a familiar example, but more complex scenarios quickly arise in enterprise applications. While Signal Forms support these patterns, introducing them before establishing a clear understanding of derived state risks turns validation back into an imperative exercise rather than a declarative one.

We have also avoided asynchronous validation. Server-backed checks introduce latency, partial failure, cancellation, and race conditions. These are not trivial concerns, and treating them casually often leads to subtle bugs and confusing user experiences. Although Signal Forms provide the necessary hooks to model asynchronous behavior, doing so responsibly requires a careful discussion of pending state, effects, and life-cycle boundaries. That discussion belongs in its own article.

Another omission is persistence and synchronization. Many forms need to autosave drafts, synchronize state with local storage, or react to changes by triggering external side effects. These behaviors are not part of the form state itself; they are consequences of state changes. Treating them as such is essential to keeping the architecture comprehensible. Introducing persistence too early would blur the distinction between state and reaction that this article has worked to establish.

Finally, this article has not addressed migration and interoperability. Few teams are starting from a blank slate. Most will adopt Signal Forms incrementally within applications that already rely on reactive forms or template-driven forms. Hybrid approaches, bridging strategies, and gradual refactors are all critical topics, but they presuppose familiarity with both paradigms. Addressing migration before establishing a solid signal-first mental model would undermine that foundation.

These omissions are intentional. A form architecture that tries to do everything at once often ends up doing nothing clearly. By focusing on the core ideas of state, derivation, and declarative validation, we create a base that can absorb additional complexity without collapsing under it.

Signal Forms in the context of Angular’s evolution

To fully appreciate Signal Forms, it helps to step back and view them not as an isolated feature, but as part of a broader shift in Angular’s design philosophy.

For much of its history, Angular emphasized declarative templates paired with imperative coordination in component classes. RxJS became the backbone of that coordination, providing a powerful abstraction for handling asynchronous workflows, user input, and external events. This model scaled well, but it also encouraged developers to express state indirectly through streams and subscriptions.

Signals represent a deliberate recalibration. They re-center Angular’s reactivity model around state and derivation, rather than events and emissions. This shift is visible across the framework: in component inputs, change detection, and now forms. Signal Forms are not an attempt to replace everything that came before; they are an attempt to make the most common use case, modeling and deriving state, simpler and more explicit.

Framed this way, the design of Signal Forms aligns more closely with state-driven form behavior. The requirement to start with a model signal reflects the idea that the state should have a single, inspectable source of truth. Schema-based validation aligns with the notion that constraints are properties of state, not behaviors triggered by events. Field state exposed as signals reinforces the idea that validity, errors, and interaction metadata are derived values that should be read, not managed.

It is also worth noting that Signal Forms do not attempt to abstract away form behavior. They do not hide form state behind opaque classes or life-cycle hooks. They do not require developers to think in terms of control hierarchies or subscription graphs. Instead, they expose form behavior directly, making it easier to reason about how values, validation, and UI feedback relate to one another.

This approach aligns closely with other recent changes in Angular, including the introduction of modern template control flow and a stronger emphasis on explicit data dependencies. Together, these features point toward a framework that favors clarity over indirection and composition over orchestration.

Importantly, Signal Forms are still evolving. Their APIs may change, and their surface area will almost certainly expand. That is precisely why grounding them in first principles matters. Developers who understand why Signal Forms work the way they do will be far better equipped to adapt as the APIs mature.

This article has intentionally avoided duplicating documentation or enumerating every available feature. Instead, it has focused on establishing a conceptual framework that makes the official APIs feel intuitive rather than surprising. When viewed this way, Signal Forms are not a new way to write forms; they are a clearer expression of what forms have always been.

A new way to think about forms

Building the registration form in this article reveals a quiet but important shift. The reduction in complexity does not come from fewer features or simpler requirements. It comes from expressing form behavior in terms of state and derivation rather than orchestration and reaction.

By treating the data model as the single source of truth, validation rules as declarative constraints, and UI behavior as derived from current conditions, much of the coordination logic that typically surrounds forms becomes unnecessary. There are fewer subscriptions to manage, fewer flags to synchronize, and fewer life-cycle concerns to reason about. Form behavior becomes easier to inspect because it is visible directly in the relationships between values.

This approach does not eliminate the hard problems associated with forms. Asynchronous validation, persistence, and interoperability with existing Angular Forms APIs still require careful design. What changes is where that complexity lives. Instead of being interwoven with state representation, those concerns are layered explicitly on top of a clear foundation.

Signal-first forms are not a universal replacement for existing patterns, nor are they a shortcut to simpler applications. They are, however, a strong example of how aligning APIs with first principles can reduce cognitive overhead and improve maintainability over time. For teams building large, state-heavy forms, this alignment can make the difference between code that merely works and code that continues to evolve without friction.

Building a signal-first form in Angular

18 June 2026 at 10:00

Understanding a reactivity model in the abstract is useful, but it is ultimately incomplete without seeing how it shapes real application code. Concepts such as state, derivation, and explicit dependencies only become meaningful when they influence how forms are built, validated, and maintained in practice.

In two previous articles, “Angular Signal Forms: From event pipelines to signal-driven state” and “Angular Signals explained: How pull-based reactivity changes how we model state,” we reframed form behavior as a state-driven problem and examined Angular Signals as a pull-based reactivity model well-suited to that kind of work. The natural next step is to apply those ideas to an actual Angular form and observe how the architecture changes when state becomes the primary concern.

This article focuses on a concrete example: a modest but realistic registration form. Rather than introducing new concepts, the goal here is to make earlier ideas tangible. We will see how a signal-backed model reshapes validation, interaction state, and submission logic, and how much coordination logic simply disappears when form behavior is expressed declaratively.

The focus here is not on novelty or completeness, but on making the underlying ideas easier to reason about. By walking through a signal-first form from model definition to submission, we can evaluate whether this approach truly reduces complexity and where it introduces new trade-offs that teams should understand before adopting it more broadly.

Implementing a signal-first registration form

With the conceptual groundwork in place, we can now turn theory into a concrete implementation. In this section, we will build a fully working registration form using Angular’s Signal Forms API. This example is deliberately modest in scope, but it is designed to serve as the foundation for the rest of the series. Each subsequent article will extend this same example rather than introducing a new one.

The form collects an email address, a password, a confirmation password, and explicit acceptance of terms. While simple on the surface, this structure allows us to explore field-level validation, cross-field constraints, interaction state, and submission behavior, all without reverting to event-driven form logic.

Project setup and structure

The example assumes a standard Angular application created with the Angular CLI and configured to use Signals (Angular 17+). The Signal Forms APIs (Angular 21+) live under @angular/forms/signals, which must be explicitly imported.

https://github.com/sonukapoor/angular-signal-forms

The folder structure is intentionally conservative:

src/
  app/
    registration/
      registration.component.ts
      registration.component.html
      registration.model.ts

Separating the model from the component keeps form state independent of presentation. This becomes increasingly valuable as the form grows or is reused across multiple components.

Defining the form model

We begin by defining the shape of the data that the form collects. This is a plain TypeScript interface with no Angular dependencies. Treating the form model as a simple data structure reinforces the idea that the form’s values are just state.

// registration.model.ts
export interface RegistrationData {
  email: string;
  password: string;
  confirmPassword: string;
  acceptedTerms: boolean;
}

This interface mirrors what would typically be sent to a back-end API. There is no duplication of state, no separate “form value” object, and no mapping required at submission time.

Creating the signal-backed form

The form itself is created in the component using a writable signal as the source of truth. The form() function attaches form semantics validation, field state, and submission to that signal.

// registration.component.ts
import { CommonModule } from "@angular/common";
import { Component, signal } from "@angular/core";
import {
  email,
  form,
  FormField,
  required,
  submit,
} from "@angular/forms/signals";
import { RegistrationData } from "./registration.model";

@Component({
  selector: "app-registration",
  imports: [FormField, CommonModule],
  templateUrl: "./registration.html",
  styleUrl: "./registration.css",
})
export class Registration {
  readonly model = signal({
    email: "",
    password: "",
    confirmPassword: "",
    acceptedTerms: false,
  });

  readonly registrationForm = form(this.model, (schema) => {
    required(schema.email, { message: "Email is required" });
    email(schema.email, { message: "Enter a valid email address" });

    required(schema.password, { message: "Password is required" });
    required(schema.confirmPassword, {
      message: "Please confirm your password",
    });

    required(schema.acceptedTerms, {
      message: "You must accept the terms to continue",
    });
  });

  async onSubmit(event?: Event) {
    event?.preventDefault();

    await submit(this.registrationForm, (value) => {
      console.log(value());
      // Mock Server Call
      return Promise.resolve([
        {
          kind: "EmailAlreadyExists",
          field: this.registrationForm.email,
          error: { kind: "server", message: "Email already taken" },
        },
      ]);
    });
  }
}

Several design decisions are worth noting.

First, the model signal is defined as read-only. All mutations to the model occur through form bindings, not ad hoc assignments in the component. This keeps the component declarative and avoids the temptation to manipulate form state imperatively.

Second, validation is declared in one place. The schema function describes constraints on the model without introducing control trees, validator arrays, or observable pipelines. Angular takes responsibility for re-running validation whenever the model changes.

Finally, submission logic is explicit. The submit() helper ensures that the form is valid before invoking the callback, and it passes the current model value directly. There is no need to check flags or manually extract values.

Binding the form to the template

With the form defined, the next step is to bind it to the template. Signal Forms provide the [formField] directive, which connects an input element directly to a field in the form schema.





  
@if (registrationForm().errors().length > 0) {
@for (error of registrationForm().errors(); track error.message) {

{{ error.kind }}

}
}
@if ( registrationForm.acceptedTerms().invalid() && registrationForm.acceptedTerms().touched() ) {

{{ registrationForm.acceptedTerms().errors()[0].message }}

}
@if ( registrationForm.confirmPassword().invalid() && registrationForm.confirmPassword().touched() ) {

{{ registrationForm.confirmPassword().errors()[0].message }}

}
@if ( registrationForm.password().invalid() && registrationForm.password().touched() ) {

{{ registrationForm.password().errors()[0].message }}

}
@if ( registrationForm.email().invalid() && registrationForm.email().touched() ) {

{{ registrationForm.email().errors()[0].message }}

}

What stands out here is the absence of indirection. Each input binds directly to a field. Validation state is accessed through signals such as invalid() and touched(). Error messages are read from a structured error object, not reconstructed manually.

This template contains no subscriptions, no async pipes, and no event handlers for value changes. The UI simply reflects the current form state.

Interaction state and user experience

One of the common criticisms of declarative form models is that they obscure user interaction logic. Signal Forms address this directly by exposing interaction metadata as signals.

The touched() signal determines whether a field has been interacted with. By combining it with invalid(), we control when validation messages appear. This logic remains purely declarative: the template describes when errors should be visible, and Angular ensures the signals stay up-to-date.

The disabled state of the submit button is derived from registrationForm.invalid(). There is no need to manually enable or disable it in response to events. If the form becomes valid, the button is enabled automatically.

Why this scales

Even at this early stage, several advantages of a signal-first form model are apparent. The form’s behavior is expressed in terms of state and derivation, not events. The model, validation rules, and UI bindings are clearly separated. There is no duplication of logic between the component and the template.

As the form grows, this structure holds. Additional fields introduce additional schema entries and template bindings, not new subscription logic. Cross-field validation can be added declaratively. Asynchronous validation and persistence can be layered on without rewriting the core model.

Most importantly, the form remains inspectable. At any point during execution, the model signal reflects the current state of the form. Derived state validity, errors, and UI flags can be understood by reading the code, not by tracing runtime behavior.

What we did not solve yet (and why)

At this stage, it would be easy to walk away with the impression that Signal Forms eliminates most of the hard problems associated with form handling. That impression would be misleading. What we have built so far is intentionally incomplete, not because the approach falls short, but because introducing too much too early obscures the value of the underlying model.

One area we have deliberately postponed is cross-field validation that expresses richer business rules. Many real-world forms depend on relationships between fields rather than isolated constraints. Password confirmation is a familiar example, but more complex scenarios quickly arise in enterprise applications. While Signal Forms support these patterns, introducing them before establishing a clear understanding of derived state risks turns validation back into an imperative exercise rather than a declarative one.

We have also avoided asynchronous validation. Server-backed checks introduce latency, partial failure, cancellation, and race conditions. These are not trivial concerns, and treating them casually often leads to subtle bugs and confusing user experiences. Although Signal Forms provide the necessary hooks to model asynchronous behavior, doing so responsibly requires a careful discussion of pending state, effects, and life-cycle boundaries. That discussion belongs in its own article.

Another omission is persistence and synchronization. Many forms need to autosave drafts, synchronize state with local storage, or react to changes by triggering external side effects. These behaviors are not part of the form state itself; they are consequences of state changes. Treating them as such is essential to keeping the architecture comprehensible. Introducing persistence too early would blur the distinction between state and reaction that this article has worked to establish.

Finally, this article has not addressed migration and interoperability. Few teams are starting from a blank slate. Most will adopt Signal Forms incrementally within applications that already rely on reactive forms or template-driven forms. Hybrid approaches, bridging strategies, and gradual refactors are all critical topics, but they presuppose familiarity with both paradigms. Addressing migration before establishing a solid signal-first mental model would undermine that foundation.

These omissions are intentional. A form architecture that tries to do everything at once often ends up doing nothing clearly. By focusing on the core ideas of state, derivation, and declarative validation, we create a base that can absorb additional complexity without collapsing under it.

Signal Forms in the context of Angular’s evolution

To fully appreciate Signal Forms, it helps to step back and view them not as an isolated feature, but as part of a broader shift in Angular’s design philosophy.

For much of its history, Angular emphasized declarative templates paired with imperative coordination in component classes. RxJS became the backbone of that coordination, providing a powerful abstraction for handling asynchronous workflows, user input, and external events. This model scaled well, but it also encouraged developers to express state indirectly through streams and subscriptions.

Signals represent a deliberate recalibration. They re-center Angular’s reactivity model around state and derivation, rather than events and emissions. This shift is visible across the framework: in component inputs, change detection, and now forms. Signal Forms are not an attempt to replace everything that came before; they are an attempt to make the most common use case, modeling and deriving state, simpler and more explicit.

Framed this way, the design of Signal Forms aligns more closely with state-driven form behavior. The requirement to start with a model signal reflects the idea that the state should have a single, inspectable source of truth. Schema-based validation aligns with the notion that constraints are properties of state, not behaviors triggered by events. Field state exposed as signals reinforces the idea that validity, errors, and interaction metadata are derived values that should be read, not managed.

It is also worth noting that Signal Forms do not attempt to abstract away form behavior. They do not hide form state behind opaque classes or life-cycle hooks. They do not require developers to think in terms of control hierarchies or subscription graphs. Instead, they expose form behavior directly, making it easier to reason about how values, validation, and UI feedback relate to one another.

This approach aligns closely with other recent changes in Angular, including the introduction of modern template control flow and a stronger emphasis on explicit data dependencies. Together, these features point toward a framework that favors clarity over indirection and composition over orchestration.

Importantly, Signal Forms are still evolving. Their APIs may change, and their surface area will almost certainly expand. That is precisely why grounding them in first principles matters. Developers who understand why Signal Forms work the way they do will be far better equipped to adapt as the APIs mature.

This article has intentionally avoided duplicating documentation or enumerating every available feature. Instead, it has focused on establishing a conceptual framework that makes the official APIs feel intuitive rather than surprising. When viewed this way, Signal Forms are not a new way to write forms; they are a clearer expression of what forms have always been.

A new way to think about forms

Building the registration form in this article reveals a quiet but important shift. The reduction in complexity does not come from fewer features or simpler requirements. It comes from expressing form behavior in terms of state and derivation rather than orchestration and reaction.

By treating the data model as the single source of truth, validation rules as declarative constraints, and UI behavior as derived from current conditions, much of the coordination logic that typically surrounds forms becomes unnecessary. There are fewer subscriptions to manage, fewer flags to synchronize, and fewer life-cycle concerns to reason about. Form behavior becomes easier to inspect because it is visible directly in the relationships between values.

This approach does not eliminate the hard problems associated with forms. Asynchronous validation, persistence, and interoperability with existing Angular Forms APIs still require careful design. What changes is where that complexity lives. Instead of being interwoven with state representation, those concerns are layered explicitly on top of a clear foundation.

Signal-first forms are not a universal replacement for existing patterns, nor are they a shortcut to simpler applications. They are, however, a strong example of how aligning APIs with first principles can reduce cognitive overhead and improve maintainability over time. For teams building large, state-heavy forms, this alignment can make the difference between code that merely works and code that continues to evolve without friction.

Angular Signals explained: How pull-based reactivity changes how we model state

4 June 2026 at 10:00

Angular’s introduction of Signals has generated both excitement and confusion. For many developers, Signals appear to be “simpler observables” or a more convenient way to trigger updates without subscriptions. Others attempt to map them directly onto familiar RxJS patterns, expecting emissions, operators, and event-style coordination.

Both interpretations miss the point.

Signals are not primarily an event system, and they are not designed to replace RxJS. They represent a different way of modeling application behavior, one that centers on current state and explicit dependencies rather than sequences of events. This distinction is subtle at first, but it has significant consequences for how applications are structured and reasoned about over time.

In a previous article, “Angular Signal Forms: From event pipelines to signal-driven state,” we reframed form behavior as a state-driven problem rather than an event-driven one. That shift raises an important follow-up question: what kind of reactive primitive is best suited for expressing state and derived behavior? To answer that, we need to understand Signals on their own terms, independent of any specific feature such as forms.

This article examines Angular Signals as a reactivity model rather than a convenience API. By clarifying what Signals are and, just as importantly, what they are not, we can better understand where they fit alongside RxJS and why they align so naturally with state-heavy problems such as form modeling.

Signals as a state primitive (not an event system)

To understand why Signals are a good fit for form modeling, it helps to be precise about what Signals are, and just as importantly, what they are not.

Signals are not an event system. They do not represent a sequence of things that happened over time. Instead, a signal represents a current value, along with a dependency graph that describes how other values derive from it. When a signal changes, Angular does not broadcast an event. It simply marks dependent computations as stale and reevaluates them the next time they are read. This is what we mean by fine-grained change detection control.

This distinction may seem subtle, but it has profound implications for how we reason about application logic.

Reactive streams encourage developers to think in terms of emissions. When something changes, subscribers are notified, operators transform the stream, and side effects occur in response. This model is extremely powerful for asynchronous workflows, but it introduces temporal reasoning even when time is not an essential concern. Developers must ask not only what the current state is, but how it arrived there and which emission triggered a particular piece of logic.

Signals, by contrast, encourage a declarative, pull-based model. A computed signal does not react to changes as they occur. Instead, it declares that its value depends on other signals. When those dependencies change, the computed value is simply recomputed the next time it is accessed. There is no notion of subscription order, missed emissions, or stale listeners.

This pull-based model aligns naturally with form state. At any moment, a form has a well-defined set of values. From those values, validity, error messages, and UI flags can be derived. These relationships do not depend on the sequence of changes that led to the current state. They depend only on the current state itself.

This is why Signals feel simpler when applied to state-heavy problems. They shift the developer’s focus away from orchestration and toward declaration. Instead of asking “What should happen when this changes?”, the question becomes “What does this value depend on?”

It is important to note that this does not make Signals a replacement for RxJS. Angular still relies on observables for asynchronous streams, external events, and integration with APIs that produce values over time. Signals and RxJS serve different purposes. In the context of forms, Signals are best used to represent state and derived state, while RxJS remains useful for asynchronous side effects and integration points.

By keeping this distinction clear, we avoid the trap of using Signals as a less expressive event system. Instead, we use them for what they do best: modeling state in a way that is explicit, deterministic, and easy to reason about.

Designing a signal-first form model with Angular Signal Forms

Before looking at any concrete implementation, it is worth clarifying what a “signal-first” form model actually implies. The goal is not to introduce a new abstraction that replaces Angular Forms, nor is it to hide form behavior behind another layer of indirection. Instead, the intent is to reorient how form state is represented and reasoned about.

In a signal-first approach, the form’s data model is treated as the single source of truth. Signals are used to represent that state directly, rather than mirroring it through control hierarchies or intermediary objects. The form itself becomes a projection over the state, attaching semantics such as validation, interaction metadata, and submission behavior without duplicating or owning the data.

This distinction is subtle but important. Traditional form models often encourage developers to think of the form as the container of state, with values flowing in and out through events. A signal-first model reverses that relationship. State exists independently of the form, and the form derives its behavior from that state. This makes it easier to inspect, reason about, and test form behavior, because the underlying data remains explicit and accessible.

The examples in this section are therefore intentionally minimal. They are not meant to demonstrate every feature of Angular Signal Forms, but to illustrate how a state-driven representation reshapes form architecture. The emphasis is on structure and intent rather than mechanics. More detailed implementation concerns, such as asynchronous validation, persistence, and UI composition, are explored in the following article.

Once we accept that form behavior is largely derived from state, the next question becomes how that idea is expressed in Angular itself. Angular’s Signal Forms API is a direct response to this shift in thinking. Rather than modeling forms as trees of controls emitting events, Signal Forms begin with a signal-backed model and layer form behavior validation, interaction state, and submission on top of it.

The starting point is still the same: a plain data model representing the values the form collects. In a signal-first approach, this model is wrapped in a writable signal and treated as the single source of truth. There is no duplication of state between the UI and the form model, and no need to synchronize multiple representations of the same data.

From this model signal, a form instance is created using Angular’s form() function. The role of this function is not to introduce a second state container, but to attach form semantics to an existing state object. The form instance provides structured access to fields, validation results, and interaction metadata, all of which are exposed as signals.

Validation is declared through a schema function passed to form(). This schema associates validation rules directly with specific fields in the model. Built-in validators such as required() and email() express constraints declaratively, and Angular automatically reevaluates them whenever the underlying values change. Validation results are not stored imperatively; they are derived and exposed through field-level signals such as invalid(), errors(), and pending().

This design is significant because it keeps validation aligned with the mental model established earlier. Validation rules do not “run” in response to events. They describe constraints on state. When state changes, derived validation state updates automatically, without subscriptions, listeners, or life-cycle hooks.

Angular Signals Form example

A minimal example illustrates the shape of this approach. The model remains a simple interface, and the signal holds the current form values.

interface RegistrationData {
  email: string;
  password: string;
  confirmPassword: string;
  acceptedTerms: boolean;
}

The form is then created by passing this model signal into form(), along with a schema that declares validation rules.

const registrationModel = signal({
  email: '',
  password: '',
  confirmPassword: '',
  acceptedTerms: false,
});

const registrationForm = form(registrationModel, (schema) => {
  required(schema.email, { message: 'Email is required' });
  email(schema.email, { message: 'Enter a valid email address' });

  required(schema.password, { message: 'Password is required' });
  required(schema.confirmPassword, { message: 'Please confirm your password' });

  required(schema.acceptedTerms, {
    message: 'You must accept the terms to continue',
  });
});

What matters here is not the syntax, but the structure. The model signal defines what the form is. The schema defines what constraints apply. Angular takes responsibility for deriving field state and exposing it through signals that the UI can consume directly.

Each field now has a clear, inspectable state. Whether a field is valid, invalid, touched, or pending is no longer inferred by tracing event streams or subscription chains. It is available as a signal, derived from the current model and the declared rules. This makes form behavior easier to reason about, test, and debug.

Just as importantly, this model scales naturally. Cross-field validation, such as checking that two password fields match, can be expressed declaratively using schema-level logic that reads from multiple fields. Form-level state, such as whether submission should be allowed, is derived rather than toggled imperatively. The form remains a projection of the state, not a controller of behavior.

I have avoided discussing templates or DOM integration here. The purpose of this section is to show that Angular’s Signal Forms align closely with the first-principles model introduced above. They do not replace that model; they formalize it.

In the next article in this series, we will connect this signal-first form to an actual Angular component. We will bind fields to inputs, render validation feedback using field state signals, and implement submission logic. This implementation will form the foundation of the GitHub example that accompanies this series and will be extended in later articles to cover asynchronous validation, persistence, and hybrid approaches.

The power of Signals

Angular Signals represent a deliberate shift in how reactivity is expressed within the framework. Rather than focusing on events, emissions, and coordination, Signals encourage developers to describe relationships between values. Computation becomes declarative, dependencies become explicit, and behavior becomes easier to reason about by inspection rather than reconstruction.

This does not diminish the role of RxJS. Event streams, asynchronous workflows, and integration with external systems remain essential parts of modern applications. Signals and RxJS solve different problems, and treating them as interchangeable inevitably leads to confusion. When each is used for what it does best — Signals for state and derivation, RxJS for coordination and side effects — the resulting architecture becomes clearer and more maintainable.

Viewed through this lens, the appeal of Signals is not novelty, but alignment. Signals map closely to how developers already think about state: as something that exists now, from which other values can be derived deterministically. This alignment reduces cognitive overhead, particularly in parts of an application where behavior is dominated by state rather than time.

With this understanding in place, we can now turn to practice. The next article applies these ideas to a concrete Angular example, showing how a signal-first approach reshapes form modeling, validation, and UI logic without reintroducing event-driven complexity.


❌