spatie/twitter-oembed
Fetch Twitter (X) tweet embed HTML via the public oEmbed API—no developer account needed. Lightweight utility for turning tweet URLs into embeddable HTML for blogs, markdown, editors, or emails, without relying on Twitter’s heavy JavaScript widget.
Installation:
composer require spatie/twitter-oembed
No additional configuration is required—just autoload.
First Use Case: Fetch a tweet by its URL or ID:
use Spatie\TwitterOEmbed\TwitterOEmbed;
$tweet = TwitterOEmbed::create('https://twitter.com/user/status/123456789');
// or by ID:
$tweet = TwitterOEmbed::create('123456789');
Outputs a Spatie\TwitterOEmbed\TwitterOEmbed object with embedded HTML.
Where to Look First:
src/TwitterOEmbed.php for core logic (e.g., how URLs/IDs are parsed).tests/) for edge cases (e.g., invalid URLs, rate limits).Embedding Tweets in Markdown/HTML: Use the package to generate static HTML for tweets, then parse it into your CMS or markdown processor:
$html = TwitterOEmbed::create($tweetUrl)->html();
// Inject `$html` into a markdown template or rich-text field.
Caching Responses: Cache responses to avoid hitting Twitter’s rate limits (unofficial but recommended):
$cacheKey = 'tweet_'.$tweetId;
$html = cache()->remember($cacheKey, now()->addHours(1), function() use ($tweetUrl) {
return TwitterOEmbed::create($tweetUrl)->html();
});
Integration with Laravel Blade: Create a custom Blade directive or helper:
// app/Helpers/TweetHelper.php
function tweet($urlOrId) {
return TwitterOEmbed::create($urlOrId)->html();
}
Usage in Blade:
{!! tweet('https://twitter.com/user/status/12345') !!}
Batch Processing: Fetch multiple tweets in a loop (respect rate limits!):
$tweetUrls = ['url1', 'url2', 'url3'];
foreach ($tweetUrls as $url) {
$html = TwitterOEmbed::create($url)->html();
// Process or store $html
}
Rich Text Editors (e.g., TinyMCE, CKEditor): Use the package to generate embeddable HTML snippets for pasting into editors:
$editorPlugin = new TinyMCEPlugin();
$editorPlugin->addEmbedButton('Tweet', function($url) {
return TwitterOEmbed::create($url)->html();
});
Customizing Output:
Extend the TwitterOEmbed class to modify the HTML structure:
class CustomTwitterOEmbed extends \Spatie\TwitterOEmbed\TwitterOEmbed {
public function html() {
$html = parent::html();
return str_replace('class="tweet"', 'class="custom-tweet"', $html);
}
}
Fallback for Invalid Tweets: Handle missing/blocked tweets gracefully:
try {
$tweet = TwitterOEmbed::create($url);
} catch (\Spatie\TwitterOEmbed\Exceptions\InvalidTweetException $e) {
return '<p>Tweet not found or inaccessible.</p>';
}
Rate Limiting:
URL/ID Parsing:
twitter.com/user/status/123) or raw IDs (123456789), but malformed inputs (e.g., missing status/) will fail silently or return invalid HTML.if (!TwitterOEmbed::isValidUrlOrId($urlOrId)) {
throw new \InvalidArgumentException('Invalid tweet URL or ID.');
}
Deprecated API:
HTML Sanitization:
Str::of($html)->markdown() or a library like htmlpurifier.Missing Metadata:
Check Raw Response: Inspect the underlying API response for debugging:
$response = TwitterOEmbed::create($url)->getResponse();
// $response contains the raw JSON from Twitter.
Log Failed Requests: Wrap calls in a try-catch to log errors:
try {
$tweet = TwitterOEmbed::create($url);
} catch (\Exception $e) {
\Log::error("Failed to fetch tweet {$url}: " . $e->getMessage());
}
Custom API Endpoint: Override the default endpoint (e.g., for a self-hosted proxy):
TwitterOEmbed::setApiEndpoint('https://your-proxy.com/oembed');
Add Headers: Inject custom headers (e.g., for authentication if using a proxy):
TwitterOEmbed::setClient(new \GuzzleHttp\Client([
'headers' => ['X-Custom-Header' => 'value'],
]));
Mocking for Tests:
Use the TwitterOEmbed facade’s fake() method to mock responses:
TwitterOEmbed::fake([
'https://twitter.com/user/status/123' => '<div>Mock HTML</div>',
]);
Event Listeners:
Extend the package by listening to its events (e.g., CreatingTwitterOEmbed):
TwitterOEmbed::creating(function ($tweet) {
// Log or modify the tweet before embedding
});
How can I help you explore Laravel packages today?