# Web Share

The Web Share component provides an interface to the Web Share API, allowing web applications to leverage the native share functionality of the user's device. This creates a seamless sharing experience by integrating with the device's built-in sharing options (social media apps, messaging apps, email, etc.).

This component is particularly useful for:

* Sharing articles, blog posts, or news content
* Sharing product pages in e-commerce applications
* Sharing user-generated content (photos, videos, text)
* Enabling social media sharing without third-party widgets
* Sharing files directly from your web application

## Browser Support

The Web Share API is supported in most modern mobile browsers and increasingly in desktop browsers. The API requires user interaction (e.g., click) to trigger the share dialog.

{% hint style="info" %}
Always check for API support before using it, and provide a fallback for browsers that don't support the Web Share API.
{% endhint %}

## Usage

### Basic Text and URL Sharing

{% code lineNumbers="true" %}

```twig
<div {{ stimulus_controller('@pwa/web-share') }}>
    <article>
        <h1>Amazing Article Title</h1>
        <p>Read this amazing content...</p>

        <button {{ stimulus_action('@pwa/web-share', 'share', 'click', {
            title: 'Amazing Article Title',
            text: 'Check out this amazing article!',
            url: 'https://example.com/article/123'
        }) }}>
            Share Article
        </button>
    </article>
</div>

<script>
    document.addEventListener('pwa--web-share:success', () => {
        console.log('Content shared successfully!');
    });

    document.addEventListener('pwa--web-share:error', (event) => {
        console.error('Share failed:', event.detail.error);
    });
</script>
```

{% endcode %}

### Sharing Current Page

{% code lineNumbers="true" %}

```twig
<div {{ stimulus_controller('@pwa/web-share') }}>
    <button {{ stimulus_action('@pwa/web-share', 'share', 'click', {
        title: document.title,
        text: 'Check out this page!',
        url: window.location.href
    }) }}>
        Share This Page
    </button>
</div>
```

{% endcode %}

### Sharing Files

{% code lineNumbers="true" %}

```twig
<div {{ stimulus_controller('@pwa/web-share') }}>
    <input type="file" id="file-input" accept="image/*" multiple>

    <button id="share-files-btn">Share Selected Images</button>
</div>

<script>
    document.getElementById('share-files-btn').addEventListener('click', async function() {
        const fileInput = document.getElementById('file-input');
        const files = Array.from(fileInput.files);

        if (files.length === 0) {
            alert('Please select at least one file');
            return;
        }

        // Trigger share action
        const controller = document.querySelector('[data-controller="@pwa/web-share"]');
        controller.dispatchEvent(new CustomEvent('share', {
            detail: {
                params: {
                    data: {
                        title: 'My Photos',
                        text: 'Check out these photos!',
                        files: files
                    }
                }
            }
        }));
    });

    document.addEventListener('pwa--web-share:success', () => {
        alert('Files shared successfully!');
    });

    document.addEventListener('pwa--web-share:error', (event) => {
        alert('Failed to share files: ' + event.detail.error.message);
    });
</script>
```

{% endcode %}

### Social Media Share with Fallback

{% code lineNumbers="true" %}

```twig
<div {{ stimulus_controller('@pwa/web-share') }}>
    <article class="blog-post">
        <h1>{{ post.title }}</h1>
        <div class="content">{{ post.content }}</div>

        <div class="share-buttons">
            <!-- Native share button -->
            <button
                id="native-share-btn"
                class="btn-share-native"
                {{ stimulus_action('@pwa/web-share', 'share', 'click', {
                    title: post.title,
                    text: post.excerpt,
                    url: url('post_show', {id: post.id})
                }) }}
            >
                <span class="icon">📤</span> Share
            </button>

            <!-- Fallback for browsers without Web Share API -->
            <div id="share-fallback" class="share-fallback" style="display:none;">
                <a href="https://twitter.com/intent/tweet?text={{ post.title|url_encode }}&url={{ url('post_show', {id: post.id})|url_encode }}" target="_blank">
                    Twitter
                </a>
                <a href="https://www.facebook.com/sharer/sharer.php?u={{ url('post_show', {id: post.id})|url_encode }}" target="_blank">
                    Facebook
                </a>
            </div>
        </div>
    </article>
</div>

<script>
    // Show fallback if Web Share API is not supported
    if (!navigator.share) {
        document.getElementById('native-share-btn').style.display = 'none';
        document.getElementById('share-fallback').style.display = 'flex';
    }

    document.addEventListener('pwa--web-share:error', (event) => {
        // Show fallback on error if not just user cancellation
        if (event.detail.error.name !== 'AbortError') {
            document.getElementById('native-share-btn').style.display = 'none';
            document.getElementById('share-fallback').style.display = 'flex';
        }
    });
</script>
```

{% endcode %}

## Parameters

None

## Actions

### `share`

Triggers the native share dialog with the provided data. The action requires a `data` parameter containing at least one of the following:

**Required parameters (at least one):**

* `title` (string): The title of the content to be shared
* `text` (string): Text content to be shared
* `url` (string): URL to be shared
* `files` (Array of File objects): Files to be shared

```twig
{{ stimulus_action('@pwa/web-share', 'share', 'click', {
    title: 'Page Title',
    text: 'Description text',
    url: 'https://example.com'
}) }}
```

{% hint style="info" %}
The `title` parameter may be ignored by some sharing targets. Not all apps will display the title you provide.
{% endhint %}

{% hint style="warning" %}
When sharing files, the browser will download them before sharing. **Ensure files are small and of shareable types**. Refer to [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share#shareable_file_types) for acceptable file types.
{% endhint %}

### Shareable File Types

The following file types are commonly supported for sharing:

* Images: `.jpg`, `.jpeg`, `.png`, `.gif`, `.webp`
* Videos: `.mp4`, `.webm`
* Audio: `.mp3`, `.wav`, `.ogg`
* Documents: `.pdf`, `.txt`

Support varies by browser and operating system.

## Targets

None

## Events

### `pwa--web-share:success`

Dispatched when content is successfully shared (user completed the share action).

**No payload**

Example:

```javascript
document.addEventListener('pwa--web-share:success', () => {
    console.log('Content shared successfully!');
    // Show thank you message
    // Track analytics
});
```

{% hint style="info" %}
This event fires when the user selects a sharing target, not necessarily when the content is actually sent to that target.
{% endhint %}

### `pwa--web-share:error`

Dispatched when sharing fails or is cancelled by the user.

**Payload**: `{error}` - Error object or message

Example:

```javascript
document.addEventListener('pwa--web-share:error', (event) => {
    const error = event.detail.error;
    console.error('Share failed:', error);

    // Handle specific errors
    if (error.name === 'AbortError') {
        // User cancelled the share
        console.log('User cancelled sharing');
    } else if (error.name === 'NotSupportedError') {
        // API not supported, show fallback
        showFallbackShareOptions();
    }
});
```

## Best Practices

1. **Check for support**: Always verify browser support before showing share buttons
2. **Require user interaction**: The share dialog can only be triggered by user actions (clicks, touches)
3. **Provide fallbacks**: Offer traditional share buttons for browsers without Web Share API support
4. **Keep it simple**: Avoid overusing share buttons; place them strategically
5. **Validate URLs**: Ensure URLs are absolute and properly formatted
6. **File size limits**: Keep shared files small to avoid long downloads
7. **Handle errors gracefully**: Always listen for error events and provide feedback

## Error Handling

Common errors you might encounter:

* **AbortError**: User cancelled the share dialog
* **NotAllowedError**: Share was not triggered by user interaction
* **NotSupportedError**: Browser doesn't support Web Share API
* **TypeError**: Invalid data provided (e.g., malformed URL)
* **DataError**: Issues with file sharing (unsupported type, too large, etc.)

## Complete Example

Here's a complete example with proper fallback handling:

{% code lineNumbers="true" %}

```twig
<div {{ stimulus_controller('@pwa/web-share') }}>
    <article class="content">
        <h1 id="content-title">My Awesome Content</h1>
        <p id="content-description">This is amazing content you should share!</p>

        <!-- Native share button -->
        <button
            id="native-share-btn"
            {{ stimulus_action('@pwa/web-share', 'share', 'click', {
                title: 'My Awesome Content',
                text: 'Check this out!',
                url: 'https://example.com/content/123'
            }) }}
            class="btn-primary"
        >
            📤 Share
        </button>

        <!-- Fallback share options -->
        <div id="fallback-share" style="display:none;">
            <button onclick="shareToTwitter()">🐦 Twitter</button>
            <button onclick="shareToFacebook()">📘 Facebook</button>
            <button onclick="copyToClipboard()">🔗 Copy Link</button>
        </div>

        <!-- Status messages -->
        <div id="share-status" class="status-message"></div>
    </article>
</div>

<script>
    const nativeBtn = document.getElementById('native-share-btn');
    const fallbackDiv = document.getElementById('fallback-share');
    const statusDiv = document.getElementById('share-status');

    // Check for Web Share API support
    if (!navigator.share) {
        nativeBtn.style.display = 'none';
        fallbackDiv.style.display = 'flex';
    }

    document.addEventListener('pwa--web-share:success', () => {
        statusDiv.textContent = '✓ Shared successfully!';
        statusDiv.className = 'status-message success';
        setTimeout(() => statusDiv.textContent = '', 3000);
    });

    document.addEventListener('pwa--web-share:error', (event) => {
        const error = event.detail.error;

        if (error.name === 'AbortError') {
            // User cancelled - don't show error
            return;
        }

        statusDiv.textContent = '✗ Share failed. Please try another method.';
        statusDiv.className = 'status-message error';

        // Show fallback options
        nativeBtn.style.display = 'none';
        fallbackDiv.style.display = 'flex';
    });

    function shareToTwitter() {
        const text = encodeURIComponent('Check this out!');
        const url = encodeURIComponent('https://example.com/content/123');
        window.open(`https://twitter.com/intent/tweet?text=${text}&url=${url}`, '_blank');
    }

    function shareToFacebook() {
        const url = encodeURIComponent('https://example.com/content/123');
        window.open(`https://www.facebook.com/sharer/sharer.php?u=${url}`, '_blank');
    }

    async function copyToClipboard() {
        try {
            await navigator.clipboard.writeText('https://example.com/content/123');
            statusDiv.textContent = '✓ Link copied to clipboard!';
            statusDiv.className = 'status-message success';
            setTimeout(() => statusDiv.textContent = '', 3000);
        } catch (err) {
            statusDiv.textContent = '✗ Failed to copy link';
            statusDiv.className = 'status-message error';
        }
    }
</script>
```

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://pwa.spomky-labs.com/symfony-ux/web-share.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
