<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Fatuma Abdullahi</title>
    <description>The latest articles on DEV Community by Fatuma Abdullahi (@fatuma).</description>
    <link>https://dev.to/fatuma</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1272398%2F194d2f3a-96bf-4058-bf51-b5df6676f949.png</url>
      <title>DEV Community: Fatuma Abdullahi</title>
      <link>https://dev.to/fatuma</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fatuma"/>
    <language>en</language>
    <item>
      <title>How to Build Accessible Video Experiences</title>
      <dc:creator>Fatuma Abdullahi</dc:creator>
      <pubDate>Mon, 28 Apr 2025 16:34:03 +0000</pubDate>
      <link>https://dev.to/getstreamhq/how-to-build-accessible-video-experiences-588f</link>
      <guid>https://dev.to/getstreamhq/how-to-build-accessible-video-experiences-588f</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj842otc1g17ukyxqn864.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj842otc1g17ukyxqn864.png" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Roughly 16% of the human population suffers from some form of long-term disability. If you include temporary disability like a broken arm, that number shoots up significantly.&lt;/p&gt;

&lt;p&gt;If your &lt;a href="https://getstream.io/video/" rel="noopener noreferrer"&gt;in-app video&lt;/a&gt; content isn't accessible, you're potentially excluding a large part of the population and missing an opportunity to create better experiences for everyone.&lt;/p&gt;

&lt;p&gt;As more people consume information through media, it is increasingly crucial to plan, organize, and ensure that video content is accessible. Accessible video expands the user base, enhances the experience for existing users, and ensures compliance with &lt;a href="https://commission.europa.eu/strategy-and-policy/policies/justice-and-fundamental-rights/disability/union-equality-strategy-rights-persons-disabilities-2021-2030/european-accessibility-act_en#:~:text=European%20accessibility%20act-,The%20European%20accessibility%20act%20is%20a%20directive%20that%20aims%20to,EU%20leading%20to%20costs%20reduction" rel="noopener noreferrer"&gt;forthcoming accessibility laws&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But what does accessibility mean in the context of video?&lt;/p&gt;

&lt;p&gt;Many teams struggle with where to start and find integrating inclusive practices challenging. This blog will break down the guidelines and considerations for building video experiences that people of all abilities can enjoy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Web Accessibility
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Accessibility is a broad topic and can be overwhelming for teams to plan for and implement. &lt;a href="https://www.w3.org/" rel="noopener noreferrer"&gt;The World Wide Web Consortium (W3C)&lt;/a&gt; is an international organization that creates web standards around accessibility, internationalization, and privacy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Through the &lt;a href="https://www.w3.org/WAI/" rel="noopener noreferrer"&gt;Web Accessibility Initiative (WAI)&lt;/a&gt;, they create and maintain a set of guidelines and resources known as &lt;a href="https://www.w3.org/WAI/standards-guidelines/" rel="noopener noreferrer"&gt;Web Content Accessibility Guidelines (WCAG&lt;/a&gt;). Organizations can use WCAG to benchmark their product's accessibility levels.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.w3.org/WAI/fundamentals/" rel="noopener noreferrer"&gt;Web accessibility&lt;/a&gt;, in particular, refers to how easy it is for people of all abilities to use a web-based product. Video accessibility refers to the same concept for audiovisual experiences. It includes a set of checks and guidelines designed to help determine what is objectively accessible for all audiences.&lt;/p&gt;

&lt;h3&gt;
  
  
  The POUR principles
&lt;/h3&gt;

&lt;p&gt;At the heart of web accessibility is the idea that the web is for everyone, and everyone should be able to use it with as little friction as possible. WCAG has a lot of guidelines and rules, but these are the main principles informing the specific implementations:&lt;/p&gt;

&lt;h4&gt;
  
  
  Perceivable
&lt;/h4&gt;

&lt;p&gt;User interfaces (UIs) and content should be seen, heard, understandable, or otherwise perceivable through multiple senses. For example, your UI should be usable by both sighted and non-sighted users.&lt;/p&gt;

&lt;p&gt;You can achieve this by developing your UI to make it easy for screen readers to scan and relay information back to blind users.&lt;/p&gt;

&lt;p&gt;Provide captions and transcripts as needed for video content to make it perceivable by those who cannot take in visual information.&lt;/p&gt;

&lt;p&gt;Here is an example of what captions look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc8lrnr300i31axuvabae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc8lrnr300i31axuvabae.png" alt="YouTube video screenshot with a dark arrow pointing to the captions: " width="606" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Other considerations for making your UI perceivable include making it responsive, handling zoom gracefully, and ensuring text is large enough and has enough contrast to increase readability.&lt;/p&gt;

&lt;h4&gt;
  
  
  Operable
&lt;/h4&gt;

&lt;p&gt;User interfaces (UIs), content, and navigation should be usable through multiple input devices. For example, your UI should work as expected if the user interacts with it using a keyboard instead of a trackpad or voice command instead of a mouse.&lt;/p&gt;

&lt;p&gt;You can make your UI and apps operable by ensuring logical and consistent navigation. This way, users always know where they are on the site or product and can quickly skip links or return to a previous screen, as illustrated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufea6nlu4uddyzgeq6cr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufea6nlu4uddyzgeq6cr.png" alt="YouTube video screenshot showing a timestamp of 18:13 / 21:26, with a white arrow pointing to the timestamp navigation. This allows navigation within the video to jump to specific moments. The red progress bar always indicates where the user is in the video." width="600" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additionally, content should be searchable, and callouts and alerts should not have auto timeouts.&lt;/p&gt;

&lt;h4&gt;
  
  
  Understandable
&lt;/h4&gt;

&lt;p&gt;User interfaces (UIs) and content should be easy for the primary audience to understand, regardless of their language proficiency or cognitive skills.&lt;/p&gt;

&lt;p&gt;The UI should follow established patterns to help users understand expected actions, and the content should use simple language and avoid jargon.&lt;/p&gt;

&lt;p&gt;For example, forms should have descriptive labels and helpful error messages, as illustrated. Large forms should be broken up into &lt;a href="https://css-tricks.com/how-to-create-multi-step-forms-with-vanilla-javascript-and-css/" rel="noopener noreferrer"&gt;multi-step forms&lt;/a&gt;. Video content should use everyday language and explain abbreviations or jargon when necessary.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzsj1cr21b789doj21p77.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzsj1cr21b789doj21p77.png" alt="Sign-up form with fields for username and password. A dark arrow points to the red error message that states, " width="400" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Other ways to increase your UI's understandability are to use consistent labeling for components across the app, avoid significant UI changes without alerting users, and give the users enough time before implementation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Robust
&lt;/h4&gt;

&lt;p&gt;User interfaces (UIs) should be as future-proof as possible. They should be compatible with current and future user tools. That is, they should work correctly across as many browsers and assistive technologies as possible while keeping up with future changes.&lt;/p&gt;

&lt;p&gt;This may sound complicated, but it comes down to using standard markup (HTML), &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility" rel="noopener noreferrer"&gt;standard web APIs&lt;/a&gt;, and giving names or titles when using a non-standard UI element.&lt;/p&gt;

&lt;p&gt;For example, use &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; instead of styling a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; to look and work like a button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F925wadnxs633q1884git.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F925wadnxs633q1884git.png" alt="Two code snippets compare HTML button implementations. The left shows a correct use with a semantic &amp;lt;code&amp;gt;&amp;lt;button&amp;gt;&amp;lt;/code&amp;gt; tag, marked with a green check. The right shows incorrect use by trying to mimic a button with a &amp;lt;code&amp;gt;&amp;lt;div&amp;gt;&amp;lt;/code&amp;gt; and styles. Marked with a red cross." width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What to consider when building accessible video experiences
&lt;/h2&gt;

&lt;p&gt;When &lt;a href="https://www.w3.org/WAI/media/av/" rel="noopener noreferrer"&gt;building accessible video experiences&lt;/a&gt;, there are certain aspects you need to pay particular attention to that are unique to audio-visual experiences. Video accessibility considerations are divided into the following main buckets:&lt;/p&gt;

&lt;h3&gt;
  
  
  User Research
&lt;/h3&gt;

&lt;p&gt;Step one is always to understand your users and the tools they use to consume your visual products. This will help keep your efforts away from blind compliance, allowing you the flexibility to implement guidelines that are informed and most meaningful to your users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Planning Audio-Visual Content and Media
&lt;/h3&gt;

&lt;p&gt;The next step is to add accessibility concerns at the planning stage. This entails understanding the video's goal and deciding whether it will need visual descriptions, sign language interpretation, and transcripts to support it.&lt;/p&gt;

&lt;p&gt;This helps make implementing the WCAG guidelines for audio-visual content a lot simpler.&lt;/p&gt;

&lt;h3&gt;
  
  
  Audio Descriptions (AD)
&lt;/h3&gt;

&lt;p&gt;This refers to describing videos or parts needed to understand the content in context. It is required when some actions in the video are integral to understanding the whole video, as captions or transcripts are not enough to convey the complete message.&lt;/p&gt;

&lt;p&gt;They are mainly for blind users and can be provided as part of the main script, a standalone file, or even a separate video.&lt;/p&gt;

&lt;p&gt;An example of a YouTube tutorial in which Audio descriptions would be necessary is when the instructor walks through how to create an &lt;code&gt;index.html&lt;/code&gt; page.&lt;/p&gt;

&lt;p&gt;They say, &lt;em&gt;"Open VSCode,&lt;/em&gt; &lt;em&gt;create a new file called index.html,&lt;/em&gt; and &lt;em&gt;then paste the following into it."&lt;/em&gt; However, listeners who can't see the screen don't know what "the following" means, making captions insufficient.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In that case, an audio description might be: &lt;em&gt;"The instructor opens VS Code and creates a new file named 'index.html.' They paste the following code: the opening &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; tag, then the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; section, where they paste a &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; tag with the text 'My First Web Page.' Next, they paste the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; tag, and within it, they paste a heading using the &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tag with the text 'Hello, world!'"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Captions
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.w3.org/WAI/media/av/captions/" rel="noopener noreferrer"&gt;Captions&lt;/a&gt; refer to the text version of a video's audiovisual content. They are shown in the media player and synced with the audio.&lt;/p&gt;

&lt;p&gt;Captions should be provided for deaf and hard-of-hearing users and should include a dialog transcript, nonverbal sounds like sound effects, and speaker identification where necessary.&lt;/p&gt;

&lt;p&gt;Building off the previous example, the YouTube instructor's video captions would read: &lt;em&gt;"[instructor] Open VSCode, create a new file called index.html, and then paste the following into it."&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Types of Captions
&lt;/h4&gt;

&lt;p&gt;There are three types of captions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Closed captions: Captions that the listener can turn on or off as they are separate from the video.&lt;/li&gt;
&lt;li&gt; Open captions: Captions that autoplay are embedded in the video, so the user cannot turn them off.&lt;/li&gt;
&lt;li&gt; Live captions: Captions that are transcribed in real time as the audio plays. The transcriber can be physically present or remote.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While there are tools that can help auto-create captions, results should not be relied on until they have been proofread and confirmed to be accurate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transcripts
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.w3.org/WAI/media/av/transcripts/" rel="noopener noreferrer"&gt;Transcripts&lt;/a&gt; are text versions of audio content. They include all the information needed to understand the content entirely.&lt;/p&gt;

&lt;h4&gt;
  
  
  Types of Transcripts
&lt;/h4&gt;

&lt;p&gt;Transcripts are of two types:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Basic transcripts: These are similar to captions, except they are provided separately from the video and are not auto-synced with the video as it plays.&lt;/li&gt;
&lt;li&gt; Descriptive transcripts: These include the audio descriptions integrated with the transcribed audio.\
Ideally, the user should be able to interact with it by using it to jump to relevant parts of the video.&lt;/li&gt;
&lt;li&gt; Interactive Transcripts: These are time-stamped transcripts that allow users to click on the text and jump directly to the corresponding part of the video. The time stamps are clickable and sync with the video's timeline.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;The Stream Video SDK &lt;a href="https://getstream.io/video/docs/api/transcribing/calls/" rel="noopener noreferrer"&gt;provides captions and transcripts&lt;/a&gt; in several languages and can be configured to your needs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Sign language and translation considerations
&lt;/h3&gt;

&lt;p&gt;Sign language is a gesture-based language used by the deaf community, some of whom might find text-based transcripts hard to understand.&lt;/p&gt;

&lt;p&gt;Providing a sign-language interpretation of your video content is the gold standard, especially if you know you have deaf users or if your content could be helpful to them.&lt;/p&gt;

&lt;p&gt;However, providing sign language translations might not be very direct and will require extra effort to be done correctly.&lt;/p&gt;

&lt;p&gt;Another point is internationalization. Although it is not a WCAG requirement, providing accurate captions and transcripts in widely used languages, especially the language your users commonly use, is an excellent way to make your video content more accessible.&lt;/p&gt;

&lt;h3&gt;
  
  
  User customizations (Media Players)
&lt;/h3&gt;

&lt;p&gt;Choosing a media player that provides UX accessibility features out of the box, where applicable, is a significant step up when building accessible video experiences.&lt;/p&gt;

&lt;p&gt;Accessible players support keyboard navigation and right-to-left (RTL) language support, have good contrast, and have labels.&lt;/p&gt;

&lt;p&gt;They also allow users to change the speed of the video, control how captions are shown, and &lt;a href="https://www.w3.org/WAI/media/av/player/" rel="noopener noreferrer"&gt;support interactive transcripts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, Stream's media player and SDKs allow users to turn captions on/off, customize theming, and &lt;a href="https://getstream.io/video/sdk/react/tutorial/video-calling/" rel="noopener noreferrer"&gt;cancel noise&lt;/a&gt; - all great features that improve video experiences.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing pre-existing video experiences
&lt;/h2&gt;

&lt;p&gt;It is generally challenging to return and make preexisting video content accessible, which is why pre-planning is essential. However, you can get some wins by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Providing transcripts.&lt;/li&gt;
&lt;li&gt;  Revising and providing accurate captions.&lt;/li&gt;
&lt;li&gt;  Assessing whether you need Audio Descriptions.&lt;/li&gt;
&lt;li&gt;  Optimizing the media player you use to display your video content.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, consider adding translated versions of the captions and transcripts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compliance considerations
&lt;/h2&gt;

&lt;p&gt;Accessibility isn't just best practice. It's increasingly becoming a legal requirement regulated by several acts.&lt;/p&gt;

&lt;p&gt;The following key regulations should be noted to ensure compliance:&lt;/p&gt;

&lt;h3&gt;
  
  
  Americans with Disabilities Act (ADA)
&lt;/h3&gt;

&lt;p&gt;This regulation has been interpreted by courts in the US to apply to digital products and spaces and should be considered by organizations operating within US law.&lt;/p&gt;

&lt;p&gt;This is more so for organizations that sell consumer-focused products.&lt;/p&gt;

&lt;h3&gt;
  
  
  European Accessibility Act (EAA)
&lt;/h3&gt;

&lt;p&gt;This regulation, which is mainly based on WCAG, is focused on digital accessibility. It will come into effect in 2025 and affect all organizations &lt;a href="https://commission.europa.eu/strategy-and-policy/policies/justice-and-fundamental-rights/disability/union-equality-strategy-rights-persons-disabilities-2021-2030/european-accessibility-act_en" rel="noopener noreferrer"&gt;operating within the EU&lt;/a&gt; and those wishing to target that market.&lt;/p&gt;

&lt;p&gt;Complying with these and other relevant accessibility laws protects organizations from litigation, inspires more trust, and increases profitability.&lt;/p&gt;

&lt;p&gt;Organizations can use this &lt;a href="https://www.wcag.com/compliance/european-accessibility-act/" rel="noopener noreferrer"&gt;checklist&lt;/a&gt; to measure their compliance levels and dedicate resources to preparing for the upcoming laws.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes and resources
&lt;/h2&gt;

&lt;p&gt;Building accessible video experiences requires much more than providing captions. It is a process that requires careful planning and consideration. However, improving audiovisual experiences to be more helpful to a broader audience is a worthwhile investment.&lt;/p&gt;

&lt;p&gt;Here are some resources you can use to learn more about web accessibility and making video accessible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://www.who.int/news-room/fact-sheets/detail/disability-and-health#:~:text=Key%20facts,1%20in%206%20of%20us." rel="noopener noreferrer"&gt;Some facts on disability (WHO)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://accessiblyapp.com/blog/web-accessibility-statistics/" rel="noopener noreferrer"&gt;30 Key Web Accessibility Statistics&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.w3.org/WAI/media/av/" rel="noopener noreferrer"&gt;Making Audio and Video Media Accessible (WAI)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.youtube.com/watch?v=Xqoei_G4UNM" rel="noopener noreferrer"&gt;Making Videos and Remote Sessions Accessible -- and other new W3C WAI resources&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility" rel="noopener noreferrer"&gt;Baseline (compatibility) - MDN Docs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Want to try building accessible video experiences? Check out how &lt;a href="https://getstream.io/video/" rel="noopener noreferrer"&gt;Stream's Video API&lt;/a&gt; can help you get started.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Use Supabase MCP in VSCode and Cursor</title>
      <dc:creator>Fatuma Abdullahi</dc:creator>
      <pubDate>Fri, 04 Apr 2025 16:03:55 +0000</pubDate>
      <link>https://dev.to/fatuma/how-to-use-supabase-mcp-in-vscode-and-cursor-3g1o</link>
      <guid>https://dev.to/fatuma/how-to-use-supabase-mcp-in-vscode-and-cursor-3g1o</guid>
      <description>&lt;p&gt;You've probably seen all the hype about MCPs, and you might be wondering what an MCP is or whether you should pay attention to it.&lt;/p&gt;

&lt;p&gt;This blog will explain an MCP, why it's a big deal, and how to set both the GitHub and Supabase MCPs in VSCode and Cursor so you can start using them immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is MCP?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://modelcontextprotocol.io/introduction" rel="noopener noreferrer"&gt;Model Context Protocol (MCP)&lt;/a&gt; is a new protocol from &lt;a href="https://www.anthropic.com/" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt; that defines a unified way for LLMs to interact with outside data sources.&lt;/p&gt;

&lt;p&gt;Before MCP, LLMs could only access their training data and user-provided local files. They could not directly access outside sources in real time. For example, your coding assistant could not read your database schema, so its ability to help you debug was limited.&lt;/p&gt;

&lt;h2&gt;
  
  
  How MCPs work
&lt;/h2&gt;

&lt;p&gt;MCPs have a client-server architecture. The LLM client interacts with users, and the server stores the MCP logic. The client calls the server when it needs certain information or wants to perform a specific action.&lt;/p&gt;

&lt;p&gt;MCP servers have two main concepts: tools and resources. Tools are callable functions that take inputs, perform operations, and return outputs. Resources refer to data that is available to the client and can be used by the tools as needed.&lt;/p&gt;

&lt;p&gt;For example, the GitHub MCP server has tools that allow it to list repositories, and the resources it can access include repositories, issues, and pull requests.&lt;/p&gt;

&lt;p&gt;This allows the application hosting the LLM (the client) to get real-time dynamic information and frees them from training data limitations like training cut-off dates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fll61dex982zcqmvcli9v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fll61dex982zcqmvcli9v.png" alt="Diagram showing the interaction between a client (LLM) and an MCP server. Arrows indicate requests sent from the client to the server and responses back. The server contains tools and resources." width="800" height="125"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  MCP vs API - What's the Difference?
&lt;/h2&gt;

&lt;p&gt;MCP seems a lot like traditional APIs, except that MCP is specifically designed for LLM clients and can work across all MCP clients without needing extra code or config.&lt;/p&gt;

&lt;p&gt;While APIs generally require that you write glue code to integrate client applications, each MCP server internally calls its specific APIs and handles data transformations using the standard protocol.&lt;/p&gt;

&lt;p&gt;This removes the need for service-specific code, allowing LLM applications to interact with many different systems without developers needing to create distinct integrations for each service.&lt;/p&gt;

&lt;p&gt;MCP abstracts the details of specific APIs and creates a plug-and-play layer that LLMs can use. This makes MCP scalable and reduces developers' maintenance headaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is MCP Important?
&lt;/h2&gt;

&lt;p&gt;Before MCP, you would need to create custom integrations for every external service your LLM needed to access, and you'd also have to manage these connections individually. This was time-consuming and inefficient.&lt;/p&gt;

&lt;p&gt;MCP streamlines this process by allowing LLMs to connect to multiple external systems through a single protocol. This makes it much easier to integrate new services and greatly increases the usefulness of LLM applications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/ECMJY5njbls?si=yJTmXPuxy_LFTLXb" rel="noopener noreferrer"&gt;https://youtu.be/ECMJY5njbls?si=yJTmXPuxy_LFTLXb&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Enable MCP in VSCode
&lt;/h2&gt;

&lt;p&gt;To start using MCP, you need to set up your text editor to work with it. For VSCode, you'll need to use VSCode Insiders, as it is not yet supported in the stable version. You can &lt;a href="https://code.visualstudio.com/insiders/" rel="noopener noreferrer"&gt;download it here&lt;/a&gt; and it works right along the normal version.&lt;/p&gt;

&lt;p&gt;Open the installation, open the user settings, search MCP, and ensure they are enabled. Then, click the Edit in settings.json as illustrated below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz76yg7wzs4mukkhf62by.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz76yg7wzs4mukkhf62by.png" alt="Screenshot of a settings page in a software application with search results for " width="600" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Test the MCP Connection with GitHub MCP Server
&lt;/h3&gt;

&lt;p&gt;Edit the &lt;code&gt;settings.json&lt;/code&gt; file to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "mcp": {
    "inputs": [],
    "servers": {
      "github": {
        "command": "npx",
        "args": [
          "-y",
          "@modelcontextprotocol/server-github"
        ],
        "env": {
          "GITHUB_PERSONAL_ACCESS_TOKEN": "&amp;lt;YOUR_GITHUB_TOKEN&amp;gt;"
        }
      }
    }
  },
  "chat.mcp.discovery.enabled": true,
  "editor.inlineSuggest.suppressSuggestions": true,
  "cody.suggestions.mode": "auto-edit (Beta)",
  "window.commandCenter": false,
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;"@modelcontextprotocol/server-github"&lt;/code&gt; refers to the server to which the Copilot will connect. It is part of Anthropic's exhaustive &lt;a href="https://github.com/modelcontextprotocol/servers" rel="noopener noreferrer"&gt;list of official MCP servers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Replace this &lt;code&gt;&amp;lt;YOUR_GITHUB_TOKEN&amp;gt;&lt;/code&gt; with your actual token. You can get it from GitHub developer settings as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4a8pbnggl632ayutzp8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4a8pbnggl632ayutzp8.gif" alt="A GitHub profile page featuring an image of a person named Fatuma Abdulllahi. The profile highlights their role as a software developer, FreeCodeCamp author, and open source contributor. Skills listed include Material Design, Tailwind, React, and more. Links to technical content such as a blog and YouTube channel are included." width="1024" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This adds the GitHub MCP server and gives it API access via the token. The GitHub server has over 20 tools you can use, all of which are &lt;a href="https://github.com/modelcontextprotocol/servers/blob/main/src/github/README.md" rel="noopener noreferrer"&gt;documented here&lt;/a&gt;. This server lets you do some pretty cool stuff, like creating issues on your behalf and searching public code.&lt;/p&gt;

&lt;p&gt;Now start the server from the settings start option shown below or by searching MCP, selecting List MCP, and then starting the one you need.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0nr4kxltqhjf9wiyl1y5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0nr4kxltqhjf9wiyl1y5.png" width="600" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can now open the Copilot chat, turn on Agent mode, and ask about GitHub repos. You should see it uses the configured GitHub MCP server to answer GitHub-specific questions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs3nxz9826aejxfeu4ria.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs3nxz9826aejxfeu4ria.gif" alt="GIF showing a dark-themed development environment with settings and a chat window titled " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the feature is ready, the general availability of MCP servers in the stable build of VSCode will be added.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Enable MCP in Cursor
&lt;/h2&gt;

&lt;p&gt;In Cursor, the process is almost similar. Open settings, select the MCP tab and click the Add new global MCP Server button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpievtbtxmbaups6tk1tb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpievtbtxmbaups6tk1tb.png" alt="The image shows a software interface for " width="600" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It will take you to a settings page, paste in the following to add the GitHub MCP server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-github",
        "&amp;lt;YOUR_GITHUB_TOKEN&amp;gt;"
      ]
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How to Test the MCP Connection with GitHub's MCP Server
&lt;/h3&gt;

&lt;p&gt;In the settings file above, replace &lt;code&gt;&amp;lt;YOUR_GITHUB_TOKEN&amp;gt;&lt;/code&gt; with your actual token following the process shown in the VSCode section above. The main difference is how you pass environment variables.&lt;/p&gt;

&lt;p&gt;To test it, open cursor chat and ask some questions as before. It will indicate that it uses GitHub MCP tools to answer the questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Add and Use Official Supabase MCP in VSCode
&lt;/h2&gt;

&lt;p&gt;Setting up Supabase's MCP server requires adding more config to the settings. Add this below the GitHub entry in the settings page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"supabase": {
  "command": "npx",
  "args": [
          "-y",
          "@supabase/mcp-server-supabase@latest",
          "--access-token",
          "&amp;lt;personal_access_token&amp;gt;"
        ],
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;&amp;lt;persoanl_access_token&amp;gt;&lt;/code&gt; with a Supabase access token, which &lt;a href="https://supabase.com/dashboard/account/tokens" rel="noopener noreferrer"&gt;you can get here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this case, the Supabase MCP accepts command-line arguments instead of environment variables, so we pass the access token directly. Start the server so Copilot can pick it up, and we are ready to test it.&lt;/p&gt;

&lt;p&gt;Open the Copilot chat to agent mode and ask questions about your Supabase projects. The Supabase MCP server has tools for managing projects, getting project information, and carrying out some database operations, including running raw SQL and applying migrations.&lt;/p&gt;

&lt;p&gt;You can find a full list of tools and more information in &lt;a href="https://github.com/supabase-community/supabase-mcp" rel="noopener noreferrer"&gt;this GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;💡&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/supabase-community/supabase-mcp" rel="noopener noreferrer"&gt;official Supabase MCP server&lt;/a&gt; is still in early days and could have breaking changes going forward.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Add and Use Supabase MCP in Cursor
&lt;/h2&gt;

&lt;p&gt;On the cursor side, it's pretty much the same process: you paste it into the same setting as above, just below the GitHub one, and replace the token with an actual token.&lt;/p&gt;

&lt;p&gt;Cursor should pick it up and activate it once you save and restart it. You will know all is well when you see this little green dot on the MCP settings page in Cursor:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0knov3iwqpn04zbcn7zy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0knov3iwqpn04zbcn7zy.png" alt="Screenshot of a Supabase console showing a list of available tools and commands, with an arrow pointing to the " width="600" height="155"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open the Cursor chat to agent mode and try to ask it questions about your Supabase projects as before. It should show you as it steps through the relevant tools to answer your questions.&lt;/p&gt;

&lt;p&gt;💡&lt;/p&gt;

&lt;p&gt;Note: Cursor has a limit of 40 tools and won't be able to use all available tools from all servers if the limit is exceeded. If you need more tools, consider disabling MCP servers that you will not be currently using.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes and Resources
&lt;/h2&gt;

&lt;p&gt;Here are some resources on MCPs, AI and some links I think will be useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://code.visualstudio.com/insiders/" rel="noopener noreferrer"&gt;VSCode Insiders&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://modelcontextprotocol.io/introduction" rel="noopener noreferrer"&gt;MCP Official Docs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/modelcontextprotocol/servers/" rel="noopener noreferrer"&gt;MCP Servers list&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/supabase-community/supabase-mcp" rel="noopener noreferrer"&gt;Supabase Official MCP Server&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://supabase.com/docs/guides/getting-started/mcp" rel="noopener noreferrer"&gt;Supabase MCP Offical Docs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.cursor.com/context/model-context-protocol" rel="noopener noreferrer"&gt;Cursor Docs on MCP&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>supabase</category>
      <category>supabasemcp</category>
      <category>mcp</category>
      <category>mcpserver</category>
    </item>
    <item>
      <title>4 Coffee Chats 4 Lessons</title>
      <dc:creator>Fatuma Abdullahi</dc:creator>
      <pubDate>Thu, 20 Mar 2025 11:17:43 +0000</pubDate>
      <link>https://dev.to/fatuma/4-coffee-chats-4-lessons-fi2</link>
      <guid>https://dev.to/fatuma/4-coffee-chats-4-lessons-fi2</guid>
      <description>&lt;p&gt;In the past month, Ive had the amazing opportunity to chat with tech professionals working in the open-source space.&lt;/p&gt;

&lt;p&gt;I picked up several insightful nuggets Id like to share with you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 1: On Imposter Syndrome
&lt;/h2&gt;

&lt;p&gt;The first insight was from &lt;a href="https://www.linkedin.com/in/victor-ndaba/" rel="noopener noreferrer"&gt;Victor Ndaba&lt;/a&gt;, Software Engineer at &lt;a href="https://developers.jumba.com/" rel="noopener noreferrer"&gt;Jumba&lt;/a&gt;. Victor talked about his experience speaking at developer conferences at the start of his career, which I found very intriguing. Imposter syndrome would stop most of us at that stage of our journeys, but Victor? This is what he had to say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Imposter syndrome is a symptom of learning not a syndrome.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dare to be brave and put yourself out there despite it all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 2: On Networking
&lt;/h2&gt;

&lt;p&gt;The second insight came from &lt;a href="https://www.linkedin.com/in/samueliglesias/" rel="noopener noreferrer"&gt;Samuel Iglesias&lt;/a&gt;, Partner at &lt;a href="https://www.igalia.com/" rel="noopener noreferrer"&gt;Igalia&lt;/a&gt;. Samuel discussed networking and growing your connections as a job strategy. He emphasized that your network can open up opportunities you would have never known existed and that the trust factor of a referral carries a lot of weight.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The important part of a conference is the people attending the conference&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Always attend the Hallway track at conferences and make connections.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 3: On Asking for Help
&lt;/h2&gt;

&lt;p&gt;This one was from &lt;a href="https://www.linkedin.com/in/lauralyn-watson/" rel="noopener noreferrer"&gt;Lauralyn Watson&lt;/a&gt;, Software Engineer at &lt;a href="https://wikimediafoundation.org/" rel="noopener noreferrer"&gt;Wikimedia&lt;/a&gt;. Lauralyn discussed being visible in your work and the importance of alerting your team when you get stuck, especially if you are new.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ask early and ask as soon as possible&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Take away? Dont be afraid to ask because they are no stupid questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 4: On Creative Exploration
&lt;/h2&gt;

&lt;p&gt;This insight came from &lt;a href="https://www.linkedin.com/in/jonathan-summers-muir-9a006237/" rel="noopener noreferrer"&gt;Jonny Summers&lt;/a&gt;, Product Design at &lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt;. Jonny discussed the portability of design systems and components, acquiring a &lt;a href="https://newsletter.posthog.com/p/good-taste-makes-great-products" rel="noopener noreferrer"&gt;good sense of taste in products&lt;/a&gt;, and the importance of flexibility as a tool for creative exploration.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You should own the code and you should be able to do whatever you want with it&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nothing kills creativity like too much rigidity. Im glad we agreed that design should be way closer to the code.&lt;/p&gt;

&lt;p&gt;]]&amp;gt;&lt;/p&gt;

</description>
      <category>coffeechats</category>
      <category>networking</category>
      <category>opensource</category>
      <category>community</category>
    </item>
    <item>
      <title>The Supabase Influence on my DevRel Mental Model</title>
      <dc:creator>Fatuma Abdullahi</dc:creator>
      <pubDate>Tue, 18 Mar 2025 16:50:00 +0000</pubDate>
      <link>https://dev.to/fatuma/the-supabase-influence-on-my-devrel-mental-model-2omo</link>
      <guid>https://dev.to/fatuma/the-supabase-influence-on-my-devrel-mental-model-2omo</guid>
      <description>&lt;p&gt;The first in-depth chat I had about developer relations was with &lt;a href="https://x.com/jonmeyers_io" rel="noopener noreferrer"&gt;Jon Meyers&lt;/a&gt;, DevRel at Supabase. We talked about definitions, tackling the representative of the organization dilemma, the challenges of context switching, and the upsides of always learning something new and usually cutting-edge.&lt;/p&gt;

&lt;p&gt;These conversations happened in my first official year in tech and formed the basis of my understanding of developer relations. Soon after, I joined &lt;a href="https://supabase.com/open-source/contributing/supasquad" rel="noopener noreferrer"&gt;Supasquad&lt;/a&gt;, an unofficial group of Supabase advocates, and have continued contributing to the community.&lt;/p&gt;

&lt;p&gt;My contributions range from &lt;a href="https://github.com/supabase/supabase/pulls?q=is%3Apr+author%3AFatumaA+is%3Aclosed" rel="noopener noreferrer"&gt;helping with the code&lt;/a&gt;, building a &lt;a href="https://github.com/supabase-community/flutter-auth-ui/" rel="noopener noreferrer"&gt;community package&lt;/a&gt;, creating &lt;a href="https://blog.hijabicoder.dev/" rel="noopener noreferrer"&gt;technical tutorials&lt;/a&gt;, and &lt;a href="https://x.com/XquisiteDreamer/status/1828748900616024379" rel="noopener noreferrer"&gt;organizing meetups&lt;/a&gt; to just &lt;a href="https://x.com/XquisiteDreamer/status/1901722200773623910" rel="noopener noreferrer"&gt;shouting about Supabase on socials&lt;/a&gt; 🤭. All while staying in touch with the team and seeing their DevRel, community, and open-source approach play out in real-time.&lt;/p&gt;

&lt;p&gt;It is no surprise, then, that the Supabase philosophy influences my mental model of developer relations as a whole. And given how &lt;a href="https://x.com/AntWilson/status/1656645880626257921" rel="noopener noreferrer"&gt;wildly popular Supabase has become&lt;/a&gt; with near $0 in ad spend, I think its a positive influence.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is DevRel?
&lt;/h2&gt;

&lt;p&gt;Developer relations is often defined as the link between the developers who use a product and the developers who build the product. DevRel is, therefore, more relevant and common among organizations whose product is meant for developers. For example, devTools and organizations that provide API access to their product.&lt;/p&gt;

&lt;p&gt;DevRel professionals take feedback from the developer users and send it to the product team. They advocate that this feedback be incorporated and considered when creating product roadmaps.&lt;/p&gt;

&lt;p&gt;They also take new product features and communicate their value and use to the developers, the intended users. They do this by ensuring that documentation is accessible and concise, providing technical examples and sample code, and being available to answer questions.&lt;/p&gt;

&lt;p&gt;By this, DevRel cultivates a culture and community around the product and is responsible for nurturing and keeping it healthy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Is DevRel dead? Discourse
&lt;/h2&gt;

&lt;p&gt;It is quite clear that &lt;a href="https://angelhack.com/blog/what-is-devrel/" rel="noopener noreferrer"&gt;DevRel plays an important role and is invaluable&lt;/a&gt; for developer-focussed products. But there has been discourse that periodically bubbles up about &lt;a href="https://blog.mb-consulting.dev/rip-devrel-2010-2024-why-it-died-and-how-to-stop-killing-it-0f7eb94d4aa4" rel="noopener noreferrer"&gt;whether the function is stagnating&lt;/a&gt; or just &lt;a href="https://dx.tips/zirp" rel="noopener noreferrer"&gt;straight up dying&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As long as developer-focused products exist, DevRel will always be needed. Its more a matter of evolving expectations and environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is DevRel Important?
&lt;/h2&gt;

&lt;p&gt;DevRel is important to developers because it makes the product approachable. DevRel professionals ensure seamless onboarding, and that product documents make sense.&lt;/p&gt;

&lt;p&gt;They act as a point of contact if you have trouble with the product and humanize the experience of interacting with and building with it.&lt;/p&gt;

&lt;p&gt;For the organization, DevRel professionals keep users happy and engaged while increasing product awareness among developers outside the products circles. A well-functioning DevRel team increases product trust, loyalty, and sustained growth.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Different Faces of DevRel
&lt;/h2&gt;

&lt;p&gt;DevRel is wide and is generally built upon these pillars:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Developer Advocacy - This focuses on creating technical content, building relationships with the community, and taking feedback from the community to the product team.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Developer Experience - This aims to enhance developer productivity with your tools. It generally involves aspects such as clear documentation, creating SDKs for the community, and offering dependable example code snippets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Community ManagementThis part focuses on community building and nurturing. It rewards active community members, creates ways for the community to participate, and creates a healthy community space.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Different organizations might have different titles and responsibilities for DevRel, but it will usually build upon the above pillars.&lt;/p&gt;

&lt;h2&gt;
  
  
  DevRel as Support
&lt;/h2&gt;

&lt;p&gt;There is a lot of &lt;a href="https://openstrategypartners.com/blog/devrel-vs-developer-marketing-a-guide-to-key-differences/" rel="noopener noreferrer"&gt;tech talk about whether or not DevRel is marketing or sales&lt;/a&gt;, but my personal take is that DevRel should be closer to technical support.&lt;/p&gt;

&lt;p&gt;Considering that companies that need DevRel are targeting developers, their technical support is in service of their users, and their users are developers, it makes most sense that DevRel should do support.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwqtk2c1kaebuam6bl0qa.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwqtk2c1kaebuam6bl0qa.gif" alt="A person energetically clapping and chanting on a stage, wearing a blue shirt. The background displays the word " width="1024" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes, marketing and sales are important, but I think DevRel working closely with the technical support team is the killer combo that serves all the purposes. Working with technical support will mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;They understand their paying users and pain points.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They understand the reasoning behind why some of the users are paid, and some arent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Based on this knowledge, they can better move the community down the funnel.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They can help correlate support data with their own observations from the community to inform a better product.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They can optimize onboarding materials and education materials to be more effective with the insights gained from technical support.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ultimately, both functions serve the same-ish user profile (tech support probably serves more of larger enterprise customers). They would still be more effective if they internally collaborated more often.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;Over the past year, Ive had the privilege of collaborating with DevRel teams across different organizations. The diversity of skills required to be badass at this amazes me.&lt;/p&gt;

&lt;p&gt;DevRel professionals can remain technically skilled while developing deep expertise in skills like effective communication and collaboration. This is what draws me in. I love that I can continue to develop my core technical skills while helping educate the community and influencing the developer experience.&lt;/p&gt;

&lt;p&gt;While I havent officially worked in a DevRel role, my experience contributing to communities, creating technical content, and collaborating with DevRel teams has made me appreciate the place of good DX.&lt;/p&gt;

&lt;p&gt;]]&amp;gt;&lt;/p&gt;

</description>
      <category>devrel</category>
      <category>dxmentorship</category>
      <category>supabase</category>
      <category>community</category>
    </item>
    <item>
      <title>How to Add SSR Auth to Astro with Supabase and Astro Actions</title>
      <dc:creator>Fatuma Abdullahi</dc:creator>
      <pubDate>Mon, 17 Mar 2025 17:44:25 +0000</pubDate>
      <link>https://dev.to/fatuma/how-to-add-ssr-auth-to-astro-with-supabase-and-astro-actions-4b1p</link>
      <guid>https://dev.to/fatuma/how-to-add-ssr-auth-to-astro-with-supabase-and-astro-actions-4b1p</guid>
      <description>&lt;p&gt;This blog will go through how to add authentication to a server-side rendered Astro project using actions and the Supabase SSR package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;This blog assumes that you are familiar with the following concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Web development frameworks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.freecodecamp.org/news/set-up-authentication-in-apps-with-supabase/" rel="noopener noreferrer"&gt;Basic authentication flows&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Basic Backend-as-a-Service (BaaS)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Supabase?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://supabase.com/docs" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt; is an open-source Backend-as-a-Service that builds upon Postgres. It provides common key features such as authentication, real-time, edge functions, storage, and more.&lt;/p&gt;

&lt;p&gt;Supabase offers a hosted version that makes building and scaling production-ready apps easy and a self-hostable version that gives users full control.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Astro?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.astro.build/en/getting-started/" rel="noopener noreferrer"&gt;Astro&lt;/a&gt; is a UI-agnostic web framework. By default, it renders &lt;a href="https://docs.astro.build/en/concepts/why-astro/#server-first" rel="noopener noreferrer"&gt;server-first&lt;/a&gt;, but it &lt;a href="https://docs.astro.build/en/guides/integrations-guide/#official-integrations" rel="noopener noreferrer"&gt;can be used with any UI framework&lt;/a&gt;, including &lt;a href="https://docs.astro.build/en/guides/client-side-scripts/" rel="noopener noreferrer"&gt;Astro client components&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are Astro Actions?
&lt;/h3&gt;

&lt;p&gt;Astro actions allow you to write server-side functions that can be called without explicitly setting up API routes. Out of the box, they provide many useful utilities that simplify the process of running server logic.&lt;/p&gt;

&lt;p&gt;Astro actions can be called from both client and server environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding SSR Authentication
&lt;/h2&gt;

&lt;p&gt;Server-side rendered (SSR) auth refers to handling authentication on the server. It is typically a &lt;a href="https://www.freecodecamp.org/news/set-up-authentication-in-apps-with-supabase/#how-does-authentication-work" rel="noopener noreferrer"&gt;cookie-based authentication method&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The server creates a session and stores a session ID in a cookie that is sent to the client.&lt;/p&gt;

&lt;p&gt;The client (in this case, the browser) receives the cookie and automatically includes it in future requests so the server can use it to determine if the user is authenticated.&lt;/p&gt;

&lt;p&gt;Since browsers cannot modify HTTP-only cookies and servers cannot access local storage, SSR authentication needs careful management to prevent security risks such as session hijacking and stale sessions.&lt;/p&gt;

&lt;h3&gt;
  
  
  SSR vs. SPA Authentication
&lt;/h3&gt;

&lt;p&gt;On the other hand, Single-Page Applications (SPAs), like traditional React apps, handle authentication on the client side because they don't have direct access to a server.&lt;/p&gt;

&lt;p&gt;SPAs typically use JWTs, which are stored in local storage, cookies, or session storage. When the application needs to communicate with a server, it sends these tokens in the HTTP headers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up Supabase Backend
&lt;/h2&gt;

&lt;p&gt;To start, you will need &lt;a href="https://supabase.com/dashboard/" rel="noopener noreferrer"&gt;a Supabase account&lt;/a&gt;. Then, follow the prompts to create a project. Go to the Authentication tab in the sidebar, click the Sign In / Up tab under Configuration, and enable user sign-ups. Then scroll down to Auth Providers and enable email.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz00xk2skywpruhl9nvg2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz00xk2skywpruhl9nvg2.png" alt="The image shows a configuration interface for user authentication. It includes options for user signups, such as enabling new user signups, manual linking, and anonymous sign-ins, with toggle switches. There is also a section for authentication providers, showing options like Email (enabled), Phone, and SAML 2.0 (both disabled)." width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up the Frontend
&lt;/h2&gt;

&lt;p&gt;Next, you will need to create an Astro project. Open your preferred IDE or Text editors integrated terminal and run the following command to scaffold an Astro project in a folder named ssr-auth. Feel free to use any name you like.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm create astro@latest ssr-auth&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Follow the provided prompts and choose a basic template to start with. When its done, change into the folder, then run &lt;code&gt;npm install&lt;/code&gt; to install dependencies, followed by &lt;code&gt;npm run dev&lt;/code&gt; to start the server, and your site will be available at &lt;code&gt;localhost:4321&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Set Astro to run in SSR mode by adding &lt;code&gt;output: "server",&lt;/code&gt; to the &lt;code&gt;defineConfig&lt;/code&gt; function found in the &lt;code&gt;astro.config.mjs&lt;/code&gt; file at the root of the folder.&lt;/p&gt;

&lt;p&gt;Next, &lt;a href="https://docs.astro.build/en/guides/integrations-guide/node/" rel="noopener noreferrer"&gt;add an adapter&lt;/a&gt; to create a server runtime. For this, use the node.js adapter by running this command in a terminal: &lt;code&gt;npx astro add node&lt;/code&gt;. This will add it and automatically make all relevant changes.&lt;/p&gt;

&lt;p&gt;Finally, add Tailwind for styling. Run this command in a terminal window: &lt;code&gt;npx astro add tailwind&lt;/code&gt;. Follow the prompts, and it will make any changes necessary.&lt;/p&gt;

&lt;p&gt;At this stage, your &lt;code&gt;astro.config.mjs&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// @ts-check
import { defineConfig } from "astro/config";

import node from "@astrojs/node";

import tailwindcss from "@tailwindcss/vite";

// https://astro.build/config
export default defineConfig({
  output: "server",

  adapter: node({
    mode: "standalone",
  }),

  vite: {
    plugins: [tailwindcss()],
  },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Set Up Supabase SSR
&lt;/h2&gt;

&lt;p&gt;Open an integrated terminal window and paste this command to add both the supabase-js client library and the SSR auth package to the project:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install @supabase/supabase-js @supabase/ssr&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Create an &lt;code&gt;.env&lt;/code&gt; file in the root of the project and paste this into it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUPABASE_URL=&amp;lt;YOUR_URL&amp;gt;
SUPABASE_ANON_KEY=&amp;lt;YOUR_ANON_KEY&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get the correct values from the Supabase dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffgf3oj0ej2j7u09dvdfc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffgf3oj0ej2j7u09dvdfc.png" alt="Screenshot of a " width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; In Astro, all environment variables to be accessed on the client side need to be prefixed by PUBLIC, but in this case, we will be using Astro actions that run on the server, so we wont need the prefix.&lt;/p&gt;

&lt;p&gt;Then, create a lib folder in the &lt;code&gt;src&lt;/code&gt; folder and add a &lt;code&gt;supabase.ts&lt;/code&gt; file. Paste this code into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createServerClient, parseCookieHeader } from "@supabase/ssr";
import type { AstroCookies } from "astro";

export function createClient({
    request,
    cookies,
}: {
    request: Request;
    cookies: AstroCookies;
}) {
    return createServerClient(
        import.meta.env.SUPABASE_URL,
        import.meta.env.SUPABASE_ANON_KEY,
        {
            cookies: {
                getAll() {
                    return parseCookieHeader(request.headers.get("Cookie") ?? "");
                },
                setAll(cookiesToSet) {
                    cookiesToSet.forEach(({ name, value, options }) =&amp;gt;
                        cookies.set(name, value, options)
                    );
                },
            },
        }
    );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sets up Supabase to handle &lt;a href="https://supabase.com/docs/guides/auth/server-side/creating-a-client?queryGroups=framework&amp;amp;framework=astro&amp;amp;queryGroups=environment&amp;amp;environment=astro-browser" rel="noopener noreferrer"&gt;cookies in a server-rendered application&lt;/a&gt; and exports a function that takes in the request and cookies object. The function is set up like this because Astro has three ways to access request and cookie information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Through Astros global object, which is only available in Astro pages.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Through &lt;code&gt;AstroAPIContext&lt;/code&gt; object, which is only available in Astro actions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Through &lt;code&gt;APIContext&lt;/code&gt; which is a subset of the global object and is available through API routes and middleware.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the &lt;code&gt;createClient&lt;/code&gt; function accepts the&lt;code&gt;request&lt;/code&gt; and &lt;code&gt;cookies&lt;/code&gt; objects separately to make it flexible and applicable in the different contexts in which it may need to be used.&lt;/p&gt;

&lt;p&gt;Next, create a &lt;code&gt;middleware.ts&lt;/code&gt; file in the &lt;code&gt;src&lt;/code&gt; folder and paste this into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { defineMiddleware } from "astro:middleware";
import { createClient } from "./lib/supabase";

export const onRequest = defineMiddleware(async (context, next) =&amp;gt; {
    const { pathname } = context.url;

    console.log("Middleware executing for path:", pathname);

    const supabase = createClient({
        request: context.request,
        cookies: context.cookies,
    });

    if (pathname === "/protected") {
        console.log("Checking auth for protected route");

        const { data } = await supabase.auth.getUser();

        // If no sess, redirect to index
        if (!data.user) {
            return context.redirect("/");
        }
    }

    return next();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file checks to see if there is an active user when we are on the protected route. If there is no user, it redirects to the index page, and if there is an active user, it allows the request to continue as is.&lt;/p&gt;

&lt;p&gt;Thus guarding the protected route from unauthenticated access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up the UI
&lt;/h2&gt;

&lt;p&gt;Open the &lt;code&gt;pages&lt;/code&gt; folder in the &lt;code&gt;src&lt;/code&gt; and replace the contents of &lt;code&gt;index.astro&lt;/code&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
import Layout from "../layouts/Layout.astro";
import { createClient } from "../lib/supabase";
import "../styles/global.css";

const supabase = createClient({
    request: Astro.request,
    cookies: Astro.cookies,
});

const { data } = await supabase.auth.getUser();

if (data.user) {
    return Astro.redirect("/protected");
}
---

&amp;lt;Layout&amp;gt;
    &amp;lt;section class="flex flex-col items-center justify-center m-30"&amp;gt;
        &amp;lt;h1 class="text-4xl text-left font-bold mb-12"&amp;gt;Sign In to Your Account&amp;lt;/h1&amp;gt;
        &amp;lt;form id="signin-form" class="flex flex-col gap-2 w-1/2"&amp;gt;
            &amp;lt;label for="email" class=""&amp;gt;Enter your email&amp;lt;/label&amp;gt;
            &amp;lt;input
                type="email"
                name="email"
                id="email"
                placeholder="youremail@example.com"
                class="border border-gray-500 rounded-md p-2"
                required
            /&amp;gt;
            &amp;lt;button
                type="submit"
                id="sign-in"
                class="bg-gray-600 hover:bg-gray-700 p-2 rounded-md text-white font-bold w-full cursor-pointer disabled:bg-gray-500 disabled:hover:bg-gray-500 disabled:cursor-not-allowed"
                &amp;gt;Sign In&amp;lt;/button
            &amp;gt;
        &amp;lt;/form&amp;gt;
    &amp;lt;/section&amp;gt;
&amp;lt;/Layout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the frontmatter creates a supabase server client and then uses it to check if we have an active user. It redirects based on this information. This works because the front matter runs on the server side, and the project is set to server output.&lt;/p&gt;

&lt;p&gt;The template displays a simple form with an email input. To complete it, add this below the closing &lt;code&gt;&amp;lt;/Layout&amp;gt;&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
    import { actions } from "astro:actions";

    const signInForm = document.querySelector("#signin-form") as HTMLFormElement;
    const formSubmitBtn = document.getElementById("sign-in") as HTMLButtonElement;

    signInForm?.addEventListener("submit", async (e) =&amp;gt; {
        e.preventDefault();
        formSubmitBtn!.disabled = true;
        formSubmitBtn!.textContent = "Signing in...";

        try {
            const formData = new FormData(signInForm);

            const results = await actions.signIn(formData);

            if (!results.data?.success) {
                formSubmitBtn.disabled = false;
                formSubmitBtn.textContent = "Sign In";
                return alert("Oops! Could not sign in. Please try again");
            }

            formSubmitBtn.textContent = "Sign In";
            return alert("Please check your email to sign in");
        } catch (error) {
            formSubmitBtn.disabled = false;
            formSubmitBtn.textContent = "Sign In";
            console.log(error);
            return alert("Something went wrong. Please try again");
        }
    });
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This adds some vanilla JavaScript that calls the &lt;code&gt;SignIn&lt;/code&gt; action on form submit, provides user feedback through the alerts, and manages the buttons text and disabled state. This effectively adds client-side interactivity to the page.&lt;/p&gt;

&lt;p&gt;Next, create a new &lt;code&gt;protected.astro&lt;/code&gt; file in the &lt;code&gt;pages&lt;/code&gt; folder and paste this into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
import Layout from "../layouts/Layout.astro";
import { createClient } from "../lib/supabase";
import "../styles/global.css";

const supabase = createClient({
    request: Astro.request,
    cookies: Astro.cookies,
});

const { data } = await supabase.auth.getUser();
---

&amp;lt;Layout&amp;gt;
    &amp;lt;section class="flex flex-col items-center justify-center m-30"&amp;gt;
        &amp;lt;h1 class="text-4xl text-left font-bold mb-12"&amp;gt;You are logged in!&amp;lt;/h1&amp;gt;
        &amp;lt;p class="mb-6"&amp;gt;Your user Id: {data.user?.id}&amp;lt;/p&amp;gt;
        &amp;lt;button
            id="sign-out"
            class="bg-gray-600 hover:bg-gray-700 px-4 py-2 rounded-md text-white font-bold cursor-pointer disabled:bg-gray-500 disabled:hover:bg-gray-500 disabled:cursor-not-allowed"
            &amp;gt;Sign Out&amp;lt;/button
        &amp;gt;
    &amp;lt;/section&amp;gt;
&amp;lt;/Layout&amp;gt;

&amp;lt;script&amp;gt;
    import { actions } from "astro:actions";
    const signOutBtn = document.getElementById("sign-out") as HTMLButtonElement;

    signOutBtn?.addEventListener("click", async (e) =&amp;gt; {
        e.preventDefault();
        signOutBtn!.disabled = true;
        signOutBtn!.textContent = "Signing out...";

        try {
            const results = await actions.signOut();

            if (!results.data?.success) {
                signOutBtn!.disabled = false;
                signOutBtn!.textContent = "Sign Out";
                return alert("Oops! Could not sign Out. Please try again");
            }
            return window.location.reload();
        } catch (error) {
            signOutBtn.disabled = false;
            signOutBtn.textContent = "Sign Out";
            console.log(error);
            return alert("Something went wrong. Please try again");
        }
    });
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This page gets the user data server-side in the frontmatter and displays it in the template along with a sign-out button.&lt;/p&gt;

&lt;p&gt;The JavaScript in the &lt;code&gt;script&lt;/code&gt; tags handle calling the sign-out action, user feedback, and button stat,e as in the &lt;code&gt;index.astro&lt;/code&gt; page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up Astro Actions
&lt;/h2&gt;

&lt;p&gt;Finally, add &lt;code&gt;actions&lt;/code&gt; folder in the &lt;code&gt;src&lt;/code&gt; folder and create an &lt;code&gt;index.ts&lt;/code&gt; file to hold our logic. Paste the following into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { defineAction, type ActionAPIContext } from "astro:actions";
import { z } from "astro:schema";
import { createClient } from "../lib/supabase";

const emailSignUp = async (
    {
        email,
    }: {
        email: string;
    },
    context: ActionAPIContext
) =&amp;gt; {
    console.log("Sign up action");
    try {
        const supabase = createClient({
            request: context.request,
            cookies: context.cookies,
        });

        const { data, error } = await supabase.auth.signInWithOtp({
            email,
            options: {
                emailRedirectTo: "http://localhost:4321/api/exchange",
            },
        });

        if (error) {
            console.error("Sign up error", error);
            return {
                success: false,
                message: error.message,
            };
        } else {
            console.log("Sign up success", data);
            return {
                success: true,
                message: "Successfully logged in",
            };
        }
    } catch (err) {
        console.error("SignUp action other error", err);
        return {
            success: false,
            message: "Unexpected error",
        };
    }
};

export const server = {
    signIn: defineAction({
        accept: "form",
        input: z.object({
            email: z.string().email(),
        }),
        handler: async (input, context) =&amp;gt; {
            return emailSignUp(input, context);
        },
    }),
    signOut: defineAction({
        handler: async (_, context) =&amp;gt; {
            const supabase = createClient({
                request: context.request,
                cookies: context.cookies,
            });
            const { error } = await supabase.auth.signOut();
            if (error) {
                console.error("Sign out error", error);
                return {
                    success: false,
                    message: error.message,
                };
            }
            return {
                success: true,
                message: "Successfully signed out",
            };
        },
    }),
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This action takes care of both the sign-in and the sign-out methods. A supabase server instance is created in the sign-in method, and the magic link method is used to sign in. It passes a redirect URL, which we have yet to create, and handles errors.&lt;/p&gt;

&lt;p&gt;The sign-out method calls Supabases sign-out method and handles any potential errors.&lt;/p&gt;

&lt;p&gt;The redirect URL refers to an API route that exchanges the code from the email Supabase sends for a session that Supabase handles.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;pages&lt;/code&gt; folder, add an &lt;code&gt;api&lt;/code&gt; folder and create within it &lt;code&gt;exchange.ts&lt;/code&gt; file. Paste this into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import type { APIRoute } from "astro";
import { createClient } from "../../lib/supabase";

export const GET: APIRoute = async ({ request, cookies, redirect }) =&amp;gt; {
    const url = new URL(request.url);
    const code = url.searchParams.get("code");

    if (!code) {
        return redirect("/");
    }

    const supabase = createClient({ request, cookies });
    const { error } = await supabase.auth.exchangeCodeForSession(code);

    if (error) {
        console.error("Error exchanging code for session:", error);
        return redirect("/404");
    }

    return redirect("/protected");
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This grabs the code from the URL in the magic link sent, creates a server client, and calls the &lt;code&gt;exchangeCodeForSession&lt;/code&gt; method with the code. It handles any error by redirecting to Astros built-in not-found page.&lt;/p&gt;

&lt;p&gt;Otherwise, it will redirect to the protected page as Supabase handles the session implementation details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the App
&lt;/h2&gt;

&lt;p&gt;In an integrated terminal, run &lt;code&gt;npm run dev&lt;/code&gt; to start the app, you should see this UI:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8369exg0p253olxtwake.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8369exg0p253olxtwake.png" alt="Sign-in page with a prompt to enter an email address and a " width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you try to access the &lt;code&gt;/protected&lt;/code&gt; page will redirect you back to this view until you sign in. Now, sign in, and you should get an email with a link that will redirect you to the &lt;code&gt;/protected&lt;/code&gt; page. This is what you should see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffhzph0lz21fwu96qsxqs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffhzph0lz21fwu96qsxqs.png" alt="Message display confirming user login with a user ID, featuring a " width="800" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With that, the project now uses Supabase SSR and is set.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes and Resources
&lt;/h2&gt;

&lt;p&gt;Here are some useful resources on SSR, Astro and Supabase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://supabase.com/docs/guides/auth/server-side/advanced-guide" rel="noopener noreferrer"&gt;Supabases advanced guide to SSR&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/supabase/ssr" rel="noopener noreferrer"&gt;Supabase SSR package&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.astro.build/en/reference/api-reference/#cookies" rel="noopener noreferrer"&gt;Docs on Astro Cookies&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://supabase.com/docs/guides/auth/sessions/pkce-flow" rel="noopener noreferrer"&gt;Supabase Docs on PKCE&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.astro.build/en/guides/actions/" rel="noopener noreferrer"&gt;Astro Docs on Actions&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/FatumaA/supa-ssr" rel="noopener noreferrer"&gt;Link to full repository&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>astro</category>
      <category>supabase</category>
      <category>authentication</category>
    </item>
    <item>
      <title>Next Steps: Seeking New Opportunities in Frontend, Design Systems, and DX</title>
      <dc:creator>Fatuma Abdullahi</dc:creator>
      <pubDate>Wed, 05 Feb 2025 05:43:38 +0000</pubDate>
      <link>https://dev.to/fatuma/next-steps-seeking-new-opportunities-in-frontend-design-systems-and-dx-4e2g</link>
      <guid>https://dev.to/fatuma/next-steps-seeking-new-opportunities-in-frontend-design-systems-and-dx-4e2g</guid>
      <description>&lt;p&gt;Over the last couple of months, I have been &lt;a href="https://blog.hijabicoder.dev/taking-apart-and-putting-together-wikimedias-design-system" rel="noopener noreferrer"&gt;working on migrating Wikimedias design system, Codex, to Penpot&lt;/a&gt;. So far, Ive successfully recreated over 1,000 components and variants, and Im about to start the final phase - piloting the new library and prepping reports.&lt;/p&gt;

&lt;p&gt;While this was an exciting and fun project, its a short-term project. As it wraps up, Im actively looking for new opportunities in front-end development - ideally in open source, design systems, and accessibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Backstory
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://blog.hijabicoder.dev/my-2022-reflections-dev-retro-2022" rel="noopener noreferrer"&gt;My tech career&lt;/a&gt; has always been one of exploration, curiosity, and experimentation. Over the years, I have tried UI design, Frontend, Mobile Multi-platform, Serverless, Microservices, Databases, Open-source, and Technical writing. I even took a brief detour into DevOps - and quickly turned back 😅. It was clear that it just wasnt my thing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyf31d1tq5egfz6gq9cmb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyf31d1tq5egfz6gq9cmb.gif" alt="A toddler runs down a hallway with excitement, wearing a black shirt and patterned pants, before turning around getting and going back quickly." width="300" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This enabled me to gain a broad understanding of development work and clarified what I was good at and which parts I enjoyed the most. No surprise that, as a creative, artsy, and poetic person - I have firmly come back around to frontend, design systems, and product documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Technical Achievements Im Proud Of
&lt;/h2&gt;

&lt;p&gt;During my exploration stage, I had some technical achievements that I'm proud of, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Leading the &lt;a href="https://phabricator.wikimedia.org/T374275" rel="noopener noreferrer"&gt;exploratory transition of Wikimedias Design System&lt;/a&gt; from Figma to Penpot.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Simplified authentication for Flutter and Supabase users by contributing a &lt;a href="https://github.com/supabase-community/flutter-auth-ui" rel="noopener noreferrer"&gt;helper auth library with ready-to-use widgets&lt;/a&gt;. The library has 13 contributors, 59 GitHub stars, and 29 forks. It is on &lt;a href="http://Pub.dev" rel="noopener noreferrer"&gt;Pub.dev&lt;/a&gt; and has a 73% popularity score.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/ocaml/ocaml.org/pull/2076" rel="noopener noreferrer"&gt;Added a working mobile version of the playground editor&lt;/a&gt;, making learning &lt;a href="http://Ocaml.org" rel="noopener noreferrer"&gt;Ocaml.org&lt;/a&gt; accessible to mobile users for the first time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/open-sauced/app/issues/2264" rel="noopener noreferrer"&gt;Increased web accessibility levels of OpenSauceds codebase&lt;/a&gt; by co-leading efforts with &lt;a href="https://www.linkedin.com/in/nickytonline/" rel="noopener noreferrer"&gt;Nick Taylor&lt;/a&gt;, Developer Advocate at &lt;a href="https://www.pomerium.com/" rel="noopener noreferrer"&gt;Pomerium&lt;/a&gt;, to enforce the standards via JSX-ally-plugin.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/open-sauced/docs/pulls?q=is%3Apr+author%3AFatumaA+is%3Aclosed" rel="noopener noreferrer"&gt;Added product guides to OpenSauceds documentation&lt;/a&gt; to help onboard new developers to common terms used in the open-source space.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Successfully migrated an unfamiliar JavaScript codebase over to using strict TypeScript.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optimizing database reads by breaking apart large backend functions into smaller microservices.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Community and Content Engagements Im Proud Of
&lt;/h2&gt;

&lt;p&gt;Beyond code, I also participate in the community by giving talks, writing tutorials, and organizing events. Some of the work Im most proud of includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.linkedin.com/posts/fatuma-abdullahi-6b804948_last-saturday-i-physically-spoke-at-a-devfest-activity-7267091320236634112-RTYL?utm_source=share&amp;amp;utm_medium=member_desktop" rel="noopener noreferrer"&gt;Talking about accessibility and authentication at DevFest Pwani&lt;/a&gt; and &lt;a href="https://www.youtube.com/clip/Ugkx1DGLUi4ZCwV9jufaHoJRQscy-NBbAfXI" rel="noopener noreferrer"&gt;getting featured in their highlight video&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Joining &lt;a href="https://supabase.com/open-source/contributing/supasquad" rel="noopener noreferrer"&gt;Supabases SupaSquad&lt;/a&gt; - an unofficial group of advocates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Organizing &lt;a href="https://x.com/XquisiteDreamer/status/1828748900616024379" rel="noopener noreferrer"&gt;multiple events for Supabases launch week&lt;/a&gt; across two cities, with a turnout of about 40%.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Getting accepted into Googles &lt;a href="https://blog.google/technology/developers/road-to-gde-supporting-future-google-developer-experts/" rel="noopener noreferrer"&gt;Road to GDE&lt;/a&gt; cohort 3 program&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Becoming a &lt;a href="https://www.freecodecamp.org/news/author/HijabiCoder/" rel="noopener noreferrer"&gt;FreeCodeCamp author&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Becoming a Guest Author on &lt;a href="https://css-tricks.com/author/fatumaabdullaho/" rel="noopener noreferrer"&gt;CSS-Tricks&lt;/a&gt; and &lt;a href="https://www.cerbos.dev/blog/how-to-use-cerbos-in-docker-compose-pdp-hub" rel="noopener noreferrer"&gt;Cerbos&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Becoming a volunteer community manager for &lt;a href="https://oscafrica.org/" rel="noopener noreferrer"&gt;Open Source Community Africa (OSCA)&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  So, Whats Next for my Tech Career?
&lt;/h2&gt;

&lt;p&gt;My next goal is to land a remote full-time or part-time role, ideally within the open-source ecosystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Perfect Organization
&lt;/h3&gt;

&lt;p&gt;Id be most excited to work with organizations that are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Open-source, open core, or that give back to open source in some way.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Concerned with building accessible products.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the DevTool space or that are working towards a noble goal greater than themselves. For example, Wikimedias goal is to democratize the worlds knowledge.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fully remote, hiring globally, or that are willing to offer relocation sponsorship.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Perfect Role
&lt;/h3&gt;

&lt;p&gt;Id be most excited to work on the following verticals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Frontend heavy full-stack roles&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Frontend roles that include building and maintaining design systems in code or design tools.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Frontend plus working on product documentation and tools.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Technical Developer Experience (DX) roles&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why You Should Hire Me
&lt;/h3&gt;

&lt;p&gt;Here are some reasons Id be a fit for your team:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;All-Rounded Product Developer -&lt;/strong&gt; I have a broad general understanding of the entire product lifecycle, giving me a unique perspective on building user-friendly and scalable products.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Frontend and Design Systems-&lt;/strong&gt; I have experience using modern technologies like React.js, Next.js, Astro, and Tailwind. I also have experience working with &lt;a href="https://blog.hijabicoder.dev/taking-apart-and-putting-together-wikimedias-design-system" rel="noopener noreferrer"&gt;design systems&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=nQUZAKWELr0" rel="noopener noreferrer"&gt;&lt;strong&gt;Web Accessibility Advocate&lt;/strong&gt;&lt;/a&gt; I co-led &lt;a href="https://github.com/open-sauced/app/issues/2264" rel="noopener noreferrer"&gt;accessibility improvements in OpenSauceds codebase&lt;/a&gt; and actively share &lt;a href="https://www.linkedin.com/posts/fatuma-abdullahi-6b804948_last-saturday-i-physically-spoke-at-a-devfest-activity-7267091320236634112-RTYL?utm_source=share&amp;amp;utm_medium=member_desktop" rel="noopener noreferrer"&gt;accessibility best practices&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Open-Source and Community Presence -&lt;/strong&gt; I &lt;a href="https://blog.hijabicoder.dev/" rel="noopener noreferrer"&gt;share what I learn&lt;/a&gt; and Im an active member of the tech community.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Remote experience -&lt;/strong&gt; I have professional, remote, and open-source experience. I can work in async environments and collaborate across cultures and time zones.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When Can I Start?
&lt;/h3&gt;

&lt;p&gt;Im ready to start in March 2025, when my current project at Wikimedia will be ending. You can reach me via &lt;a href="https://x.com/XquisiteDreamer" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/fatuma-abdullahi-6b804948/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="//mailto:fatuma@hijabicoder.dev"&gt;Email&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also reach out to any of these amazing open-source developers Ive collaborated with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.linkedin.com/in/sabfis/" rel="noopener noreferrer"&gt;Sabine Schmaltz&lt;/a&gt;, &lt;a href="http://OCaml.org" rel="noopener noreferrer"&gt;OCaml.org&lt;/a&gt; Maintainer, Developer Relations at &lt;a href="https://tarides.com/" rel="noopener noreferrer"&gt;Tarides&lt;/a&gt;- I worked closely with Sabine on &lt;a href="https://github.com/ocaml/ocaml.org/pulls?q=is%3Apr+author%3AFatumaA+is%3Aclosed" rel="noopener noreferrer"&gt;making front-end changes to Ocaml.orgs website&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.linkedin.com/in/dshukertjr/" rel="noopener noreferrer"&gt;Tyler Shukert&lt;/a&gt;, Developer Relations at &lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt; - I worked with Tyler to bring &lt;a href="https://github.com/supabase-community/flutter-auth-ui" rel="noopener noreferrer"&gt;Flutter Auth UI&lt;/a&gt; to life.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;If youre building in open-source, DevTools, or accessibility-focused products and need a frontend developer who bridges design and loves documentation - please reach out! And if you know someone who does, please share my profile.&lt;/p&gt;

&lt;p&gt;]]&amp;gt;&lt;/p&gt;

</description>
      <category>jobsearch</category>
      <category>jobs</category>
      <category>jobhunting</category>
      <category>frontend</category>
    </item>
  </channel>
</rss>
