There’s a big difference between building features and making them visible.
A user clicks thumbs down on your product. They add a comment. They upload a screenshot.
And then…
Nothing.
It disappears into logs. Or worse, into a database nobody checks.
That’s the problem. Feedback without visibility is dead data.
A few weeks ago, I wanted a better way.
Whenever a user submits feedback from my React SPA, I wanted it to instantly appear inside a Microsoft Teams channel.
Not as plain text. Not as raw JSON. But as a clean, rich Adaptive Card.
With:
User details
Rating
Reason
Comments
Attachments
Timestamp
Action buttons
And the best part?
No Microsoft Graph API.
No bot registration.
No OAuth headache.
Just:
React → Webhook → Teams Channel
In this article, I’ll show you exactly how.
What We’re Building
Every time a user submits feedback:
Example:
👍 Helpful or 👎 Not Helpful
We’ll send this directly into Teams:
Name
Current Page
Rating
Feedback reason
Comments
Screenshot link
Submission time
As a rich Adaptive Card. This turns Teams into your live product feedback dashboard.
That’s powerful.
Why Webhooks Instead of Microsoft Graph?
Simple. Because Graph is overkill for this.
Webhooks are perfect when:
✅ You only need to post messages
✅ You want quick setup
✅ No user auth required
✅ Lightweight integration
✅ Faster development
Graph API is for full Teams automation. Webhooks are for shipping faster.
Choose wisely.
Step 1: Create a Team in Microsoft Teams
Open Microsoft Teams.
Go to:
Teams → Join or create team
Click: Create Team
Choose: From scratch
Example:
Team Name:
App NotificationsDescription:
Live feedback notifications from React SPACreate it.
Done.
Step 2: Create a Channel
Inside your Team:
Click:
⋯ → Add channel
Example:
Channel name:
user-feedbackDescription:
Tracks live customer feedback eventsNow your structure looks like:
App Notifications
├── General
├── user-feedbackThis channel will receive all feedback.
Step 3: Create Webhook (New Microsoft Teams Way)
This changed.
Older tutorials tell you:
⋯ → ConnectorsThat’s outdated.
In 2026:
Use:
⋯ → WorkflowsSearch:
WebhookChoose:
✅ Send webhook alerts to a channel
This is the modern replacement.
Then:
Select your Team
Select your Channel
Create workflow
Microsoft will generate:
https://prod-xx.logic.azure.com/...Copy this.
Important: Treat it like a secret.
Never expose it publicly.
💡 Enjoying this article?
Every week day, I publish practical, production-ready deep dives covering Web development, System Design, Open source projects, Tech industry trends and AI Engineering and tools.
Step 4: Install Axios in React
We’ll use Axios.
Install:
npm install axiosSimple.
Step 5: Define Feedback Payload Type
Create:
feedbackWebhook.tsAdd:
interface FeedbackWebhookArgs {
userName: string;
page: string;
rating: string;
reason: string;
comments?: string;
attachmentName?: string;
attachmentURL?: string;
}This keeps our payload typed and predictable.
Step 6: Build Adaptive Card Payload
This is where the magic happens.
const buildAdaptiveCard = (
args: FeedbackWebhookArgs
): Record<string, unknown> => {
const {
userName,
page,
rating,
reason,
comments,
attachmentName,
attachmentURL,
} = args;
const isLike = rating === 'LIKE';
const containerStyle = isLike ? 'good' : 'attention';
const headerText = isLike
? '👍 App - User Feedback'
: '👎 App - User Feedback';
const submittedAt = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/Chicago',
year: 'numeric',
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
hour12: true,
timeZoneName: 'short',
}).format(new Date());
const facts = [
{ title: '👤 Name', value: userName || 'Unknown' },
{ title: '📄 Page', value: page },
{ title: '⭐ Rating', value: isLike ? 'HELPFUL' : 'NOT HELPFUL' },
{ title: '🏷️ Reasons', value: reason || 'None provided' },
{ title: '🕐 Submitted', value: submittedAt },
];
const actions = attachmentURL
? [
{
type: 'Action.OpenUrl',
title: `🖼️ ${attachmentName ?? 'View Attachment'}`,
url: attachmentURL,
},
]
: [];
return {
type: 'message',
attachments: [
{
contentType: 'application/vnd.microsoft.card.adaptive',
content: {
$schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
type: 'AdaptiveCard',
version: '1.2',
body: [
{
type: 'Container',
style: containerStyle,
items: [
{
type: 'TextBlock',
text: headerText,
weight: 'Bolder',
size: 'Medium',
wrap: true,
},
],
},
{
type: 'FactSet',
facts,
},
...(comments
? [
{
type: 'TextBlock',
text: `💬 ${comments}`,
wrap: true,
},
]
: []),
],
actions,
},
},
],
};
};This builds the rich Teams card.
Not just text. Structured information. Clean. Readable. Actionable.
Step 7: Send Webhook
Now send it.
import axios from 'axios';
export const triggerFeedbackWebhook = async (
args: FeedbackWebhookArgs
): Promise<void> => {
const webhookUrl = process.env.REACT_APP_TEAMS_WEBHOOK_URL;
if (!webhookUrl) {
console.log('Webhook URL missing');
return;
}
const payload = buildAdaptiveCard(args);
try {
await axios.post(webhookUrl, payload, {
headers: {
'Content-Type': 'application/json',
},
});
console.log('Webhook sent successfully');
} catch (error) {
console.error('Failed to send webhook', error);
}
};Simple. Production-friendly.
Step 8: Trigger From React UI
Imagine this is your feedback component:
const handleFeedbackSubmit = async () => {
await triggerFeedbackWebhook({
userName: 'Vijay Deepak',
page: '/dashboard',
rating: 'DISLIKE',
reason: 'UI is confusing',
comments: 'Navigation is difficult to understand',
attachmentName: 'Issue Screenshot',
attachmentURL: 'https://example.com/screenshot.png',
});
};When triggered:
Your Teams channel instantly gets:
👎 User Feedback
With all metadata.
That’s real-time collaboration.
What Makes This Better?
Most developers send:
{
"text": "Feedback received"
}That works. But it’s weak.
Adaptive Cards give:
✅ Better readability
✅ Rich formatting
✅ Action buttons
✅ Structured facts
✅ Attachments
✅ Cleaner alerts
This matters when your channel gets noisy. Readable wins.
Real Use Cases
This pattern isn’t limited to feedback.
Use it for:
Product feedback
Like this article.
Error alerts
🔥 API failedDeployment notifications
🚀 Build deployedPayment failures
❌ Transaction failedNew customer signup
🎉 New user onboardedAnalytics reports
📊 Weekly usage summarySecurity Best Practices
Never hardcode:
const webhookUrl = "..."Use:
REACT_APP_TEAMS_WEBHOOK_URL=...Even better: Use backend proxy.
Example:
React SPA → Node API → Teams WebhookWhy?
Because exposed webhooks can be abused.
Protect them Always.
Final Thoughts
The best products don’t just collect data. They route it to the right people instantly. That’s the real value here.
Your React app already knows:
what users like
what they hate
what breaks
what needs fixing
The only question is: Who sees it?
With Microsoft Teams webhooks:
The answer becomes: Everyone who needs to.
And that changes how fast teams move. Because feedback delayed is feedback wasted.
Ship smarter 🚀
Thank You for Reading!
I hope you found it helpful and informative. If you have any questions or feedback, feel free to leave a comment below. Your support and engagement mean a lot to me.
