RianNegreiros

  • Blog
  • Projetos
  • Currículo

Site Logo
  • Blog
  • Projetos
  • Currículo
  • RSS Feed
© 2025 riannegreiros.xyz. Todos os direitos reservados.
LinkedIn

Compartilhamento nativo com a Web Share API em projetos Next.js

Publicado 18 de fevereiro de 2025

Imagem de capa do post: Compartilhamento nativo com a Web Share API em projetos Next.js

Neste artigo, demonstrarei como implementei um botão de compartilhamento nativo para dispositivos móveis na página de artigos do blog do meu site pessoal usando a Web Share API e, ao mesmo tempo, mantendo os botões tradicionais de compartilhamento social para o usuário desktop.

O que é a API de compartilhamento da Web?

A Web Share API permite que os sites compartilhem textos, links e arquivos usando a caixa de diálogo de compartilhamento nativa do sistema. Essa API proporciona uma experiência mais perfeita em dispositivos móveis em comparação com os widgets de compartilhamento tradicionais de terceiros.

Por que usar a Web Share API?

  • Experiência nativa no em dispositivos moveis.
  • Integra-se diretamente aos aplicativos instalados
  • Não há necessidade de bibliotecas JavaScript externas.

Implementação da API de compartilhamento da Web em um blog Next.js

1. Configuração do componente Share Button

No meu blog, criei um componente ShareButton para lidar com a Web Share API (para usuários móveis) e com botões de compartilhamento social (para usuários de desktop).

2. Visão geral do componente

  • Para usuários de dispositivos móveis, ele exibe um botão flutuante que aciona navigator.share().
  • Para usuários de desktop, ele fornece um menu suspenso com várias opções de compartilhamento usando next-share.

ShareButton.tsx

1'use client'
2
3import { Share2 } from 'lucide-react'
4import {
5  LinkedinShareButton,
6  TwitterShareButton,
7  EmailShareButton,
8  TelegramShareButton,
9  WhatsappShareButton,
10  FacebookShareButton,
11} from 'next-share'
12import { Button } from '@/components/ui/button'
13import {
14  DropdownMenu,
15  DropdownMenuContent,
16  DropdownMenuItem,
17  DropdownMenuTrigger,
18} from '@/components/ui/dropdown-menu'
19import { useEffect, useState } from 'react'
20
21type tParams = {
22  slug: string
23  title: string
24  body: string
25}
26
27export default function ShareButton(props: { params: tParams }) {
28  const baseUrl = process.env.NEXT_PUBLIC_BASE_URL
29  const { slug, title, body } = props.params
30
31  const shareOptions = [
32    {
33      name: 'LinkedIn',
34      Component: LinkedinShareButton,
35      url: `${baseUrl}/posts/${slug}`,
36      ariaLabel: 'Compartilhar no LinkedIn',
37    },
38    {
39      name: 'Twitter',
40      Component: TwitterShareButton,
41      url: `${baseUrl}/posts/${slug}`,
42      title: title,
43      ariaLabel: 'Compartilhar no Twitter',
44    },
45    {
46      name: 'Email',
47      Component: EmailShareButton,
48      url: `${baseUrl}/posts/${slug}`,
49      title: title,
50      body: body,
51      ariaLabel: 'Compartilhar por Email',
52    },
53    {
54      name: 'WhatsApp',
55      Component: WhatsappShareButton,
56      url: `${baseUrl}/posts/${slug}`,
57      title: title,
58      separator: ':: ',
59      ariaLabel: 'Compartilhar no WhatsApp',
60    },
61    {
62      name: 'Telegram',
63      Component: TelegramShareButton,
64      url: `${baseUrl}/posts/${slug}`,
65      title: title,
66      ariaLabel: 'Compartilhar no Telegram',
67    },
68    {
69      name: 'Facebook',
70      Component: FacebookShareButton,
71      url: `${baseUrl}/posts/${slug}`,
72      title: title,
73      ariaLabel: 'Compartilhar no Facebook',
74    },
75  ]
76
77  const [isMobile, setIsMobile] = useState(false)
78
79  useEffect(() => {
80    const checkMobile = () => {
81      if (typeof window !== 'undefined') {
82        setIsMobile(/Mobi|Android|iPhone/i.test(navigator.userAgent))
83      }
84    }
85    checkMobile()
86  }, [])
87
88  const handleNativeShare = async () => {
89    if (navigator.share) {
90      await navigator.share({
91        title,
92        text: body,
93        url: `${baseUrl}/posts/${slug}`,
94      })
95    }
96  }
97
98  return isMobile ? (
99    <Button
100      onClick={handleNativeShare}
101      variant="outline"
102      className="fixed bottom-4 right-4 rounded-full"
103    >
104      <Share2 className="h-4 w-4" />
105    </Button>
106  ) : (
107    <DropdownMenu>
108      <DropdownMenuTrigger asChild>
109        <Button
110          variant="outline"
111          size="icon"
112          className="fixed bottom-4 right-4 rounded-full"
113        >
114          <Share2 className="h-4 w-4" />
115          <span className="sr-only">Open share menu</span>
116        </Button>
117      </DropdownMenuTrigger>
118      <DropdownMenuContent align="end">
119        {shareOptions.map((option) => (
120          <DropdownMenuItem key={option.name}>
121            <option.Component
122              url={option.url}
123              title={option.title}
124              body={option.body}
125              separator={option.separator}
126              aria-label={option.ariaLabel}
127            >
128              {option.name}
129            </option.Component>
130          </DropdownMenuItem>
131        ))}
132      </DropdownMenuContent>
133    </DropdownMenu>
134  )
135}

Adição do botão Share à página de post do blog

Para integrar o ShareButton à página de postagem do blog, busco os detalhes da postagem usando o Sanity.io e passo os dados relevantes para o ShareButton.

PostContent.tsx

1export default function PostContent({ params }: { params: Promise<{ slug: string }> }) {
2  const [data, setData] = useState<post | null>(null)
3  const [isLoading, setIsLoading] = useState(true)
4  const { resolvedTheme } = useTheme()
5  const router = useRouter()
6
7  useEffect(() => {
8    const fetchData = async () => {
9      setIsLoading(true)
10      const { slug } = await params
11      const fetchedData = await getData(slug)
12
13      if (!fetchedData) {
14        router.push('/not-found')
15      } else {
16        setData(fetchedData)
17      }
18      setIsLoading(false)
19    }
20    fetchData()
21  }, [params])
22
23  if (isLoading || !data) {
24    return <Loading />
25  }
26
27  const shareParams = {
28    slug: data.slug.current,
29    body: `Check out this article: ${data.title}. Read more at:`,
30    title: data.title,
31  }
32
33  return (
34    <div className="container mx-auto px-4 py-8">
35      <article className="max-w-4xl mx-auto">
36        <h1 className="text-4xl font-bold mb-4">{data.title}</h1>
37        <p className="text-muted-foreground mb-8">
38          Published {formatDate(data.firstPublishedDate)}
39        </p>
40
41        <div className="lg:grid lg:grid-cols-[1fr_250px] lg:gap-8">
42          <div className="prose prose-lg dark:prose-invert">
43            <PortableText value={data.content} components={PortableTextComponent} />
44          </div>
45          <aside className="mt-8 lg:mt-0">
46            <div className="sticky top-4">
47              <TableOfContents className="hidden lg:block" headings={data.headings} />
48            </div>
49          </aside>
50        </div>
51      </article>
52
53      <ShareButton params={shareParams} />
54    </div>
55  )
56}

Conclusão

Ao implementar a Web Share API, oferecemos uma experiência de compartilhamento nativa para usuários móveis e, ao mesmo tempo, mantemos botões de compartilhamento social amigável para desktop. Essa abordagem garante:

  • Integração móvel perfeita
  • Melhor experiência do usuário

Se estiver criando um blog com o Next.js, considere adicionar a API Web Share para obter uma maneira moderna de compartilhar conteúdo!

Tabela de Conteúdos

  • O que é a API de compartilhamento da Web?
  • Por que usar a Web Share API?
  • Implementação da API de compartilhamento da Web em um blog Next.js
  • 1. Configuração do componente Share Button
  • 2. Visão geral do componente
  • ShareButton.tsx
  • Adição do botão Share à página de post do blog
  • PostContent.tsx
  • Conclusão