bp

Apollo Elements v3.0.0 - GraphQL Meets Web Components

Apollo Elements logo featuring a rocket and moon representing GraphQL-powered web components

🚀 I'm excited to announce Apollo Elements v3.0.0—the first major release in over three years!

After a long hiatus since v2.0.0 in February 2022, Apollo Elements is back with Apollo Client 4 support, modern tooling, and a refined developer experience for building GraphQL-powered web components.

🤔 What is Apollo Elements?

Apollo Elements is a library that brings GraphQL to web components using Apollo Client. It provides reactive controllers, base classes, and ready-to-use components that let you build data-driven UIs with web standards.

The core value proposition? True portability. Web components work everywhere—Angular, React, Vue, Svelte, vanilla JavaScript, or any framework you can think of. Write your GraphQL components once using web standards, and they'll work across your entire stack.

What's New in v3

Apollo Client 4 Support

Updated to support the latest Apollo Client with all its improvements to error handling, caching, and overall performance. If you're already on Apollo Client 4, the migration should be smooth.

Node.js 24

Leveraging the latest LTS release for modern JavaScript features and better performance across the board.

Streamlined Subscription API

Subscription controllers now use a single error field instead of an errors array, matching Apollo Client's patterns and simplifying error handling:

// Before (v2)
subscription.errors; // readonly GraphQLFormattedError[]

// After (v3)
subscription.error; // Error | null

11 Packages Shipped

The release includes coordinated updates across all packages:

💻 Code Examples

Reactive Controllers (Framework-Agnostic)

The reactive controller approach works with any web component library:

import { ApolloQueryController } from '@apollo-elements/core';
import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';

const UserQuery = gql`
  query UserQuery($id: ID!) {
    user(id: $id) {
      id
      name
      avatar
      bio
    }
  }
`;

@customElement('user-profile')
class UserProfile extends LitElement {
  query = new ApolloQueryController(this, UserQuery, {
    variables: { id: this.userId }
  });

  render() {
    const { data, loading, error } = this.query;

    if (loading) return html`<loading-spinner></loading-spinner>`;
    if (error) return html`<error-message .error="${error}"></error-message>`;

    return html`
      <article>
        <img src="${data.user.avatar}" alt="${data.user.name}">
        <h2>${data.user.name}</h2>
        <p>${data.user.bio}</p>
      </article>
    `;
  }
}

Declarative HTML Components

For those who prefer a fully declarative approach, the HTML components let you write GraphQL in pure markup:

<apollo-client uri="https://api.example.com/graphql">
  <apollo-query>
    <script type="application/graphql">
      query Users {
        users {
          id
          name
          avatar
        }
      }
    </script>
    <template>
      <style>
        .user-grid {
          display: grid;
          grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
          gap: 1rem;
        }
        .user-card {
          padding: 1rem;
          border: 1px solid #ccc;
          border-radius: 8px;
        }
      </style>
      <div class="user-grid">
        <template type="repeat" repeat="{{ data.users }}">
          <div class="user-card">
            <img src="{{ item.avatar }}" alt="">
            <h3>{{ item.name }}</h3>
          </div>
        </template>
      </div>
    </template>
  </apollo-query>

  <apollo-mutation refetch-queries="Users">
    <script type="application/graphql">
      mutation AddUser($name: String!, $avatar: String) {
        addUser(name: $name, avatar: $avatar) {
          id
          name
          avatar
        }
      }
    </script>
    <template>
      <form>
        <label>
          Name
          <input data-variable="name" required>
        </label>
        <label>
          Avatar URL
          <input data-variable="avatar" type="url">
        </label>
        <button ?disabled="{{ loading }}">Add User</button>
        <small ?hidden="{{ !data }}">{{ data.addUser.name }} added!</small>
      </form>
    </template>
  </apollo-mutation>
</apollo-client> 

🎯 Why Web Components + GraphQL?

Over the years, I've found this combination particularly powerful for:

  1. Framework-agnostic component libraries - Build once, use in React, Vue, Angular, or vanilla JS
  2. Micro-frontends - Share GraphQL components across different framework-based applications
  3. Progressive enhancement - Start with server-rendered HTML, enhance with client-side GraphQL
  4. Long-term stability - Web standards don't churn like framework APIs

🚀 Getting Started

Quick Start

npm init @apollo-elements

This interactive CLI will scaffold a new project with your choice of web component library.

Manual Installation

npm install @apollo-elements/core @apollo/client graphql

Or with your preferred library integration:

npm install @apollo-elements/lit-apollo lit
npm install @apollo-elements/fast @microsoft/fast-element
npm install @apollo-elements/haunted haunted
# ... etc

📚 Resources

Demo Apps

🙏 Acknowledgements

This release wouldn't be possible without the amazing web components and GraphQL communities. Special thanks to:

🔮 What's Next?

I'm excited to see what the community builds with v3. If you're using Apollo Elements in production, have questions, or want to share what you've built, I'd love to hear from you!

Let's build something great together! 🌑🚀🌜