Publicado 18 de fevereiro de 2025
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.
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.
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).
navigator.share()
.next-share
.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}
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
.
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}
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:
Se estiver criando um blog com o Next.js, considere adicionar a API Web Share para obter uma maneira moderna de compartilhar conteúdo!