Alpheus-godswill1
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    ![Scalable-Strapi-React Apps with Typescript_ Best Practices](https://hackmd.io/_uploads/HJfknWEyR.jpg) ## Introducing TypeScript and Strapi for dynamic content handling First, we will talk about the typical problems that arise when working with dynamic content in React apps that are powered by Strapi. The purpose of this section is to lay the groundwork for understanding the significance of dynamic content management in creating systems that are scalable and maintainable. I will present TypeScript types and unions as essential resources for managing unpredictability in data structures and guaranteeing type safety, especially in the context of dynamic content handling. React has emerged as a powerful tool in the quickly changing web development space for designing dynamic and captivating user interfaces. But making the most of React when paired with a robust content management system like Strapi becomes even more important. Together, they form a formidable team that makes it easier for developers to create scalable, maintainable systems that easily handle dynamic material. In this comprehensive article, we want to explore the intricate realm of dynamic content management with the use of TypeScript and Strapi. We'll go through common issues with Strapi-powered React apps, highlighting the vital importance of efficient content management strategies. ## Goal In this piece, we aim to do more than just solve problems. Rather, you will learn how to make use of unions and TypeScript types, which are tools that help you navigate the complexities of dynamic content. These technologies ensure type safety and provide unparalleled flexibility when working with variable data structures, especially in the context of dynamic content management. So take a seat, and let's discuss the fundamentals of dynamic content management, where TypeScript and Strapi work together to build manageable, scalable, and ultimately pleasurable user experiences. ### Prerequisite To maximize the value of this article you should have a basic understanding of the following before diving into the particulars of managing dynamic content with TypeScript and Strapi: 1. Fundamental understanding of React development: Understanding the principles of React, such as data models, state management, and JSX syntax, can help you better understand the ideas covered in this article. 2. Prior exposure to Strapi is optional, however, it will help you better understand the features and specific functionalities addressed in relation to dynamic content management. 3. Knowledge of TypeScript ideas: To properly appreciate how TypeScript is used to handle dynamic content in React apps, one should have a fundamental understanding of TypeScript concepts like types, interfaces, and unions. 4. Knowledge of the fundamentals of using the terminal to perform tasks. Your workflow will be improved as a result, and you will be able to actively participate in the article without experiencing feelings of being overwhelmed. ## TypeScript types and unions This section will discuss TypeScript types and unions, which are useful tools for managing unpredictability in data structures and guaranteeing type safety, especially when handling dynamic content. ### Understanding TypeScript types Variables, parameters, and return values can include different sorts of data, and TypeScript types offer a mechanism to explicitly define these types of data. Take a look at this straightforward example of establishing types for a user object: ``` type User = { id: number; name: string; email: string; }; ``` In the example above, we have a *User* type with the attributes *name*, *email*, and *id* all of which have distinct data types, which are defined. Type safety is provided by utilizing this type, which guarantees that any variable defined as a *User* must follow this structure. ### Managing variability with TypeScript unions We can represent a value that can be any of numerous different kinds using TypeScript unions. Let's use an example where a function that takes in a string or a number as an input to demonstrate this: ``` function displayData(data: string | number) { console.log(data); } // Usage examples displayData("Hello"); // Output: Hello displayData(42); // Output: 42 ``` In the example above, the displayData function can accept either a string or a number, ensuring type safety while allowing for variability in the input data. ### Ensuring type safety in dynamic content handling We can maintain type safety while controlling the unpredictability present in dynamic content handling scenarios by utilizing TypeScript types and unions. Let's look at an illustration of using dynamic data structures to handle Application Programming Interface (API) responses: ``` type ApiResponse = { status: "success" | "error"; data: string | { id: number; name: string }; }; // Example API response const response: ApiResponse = { status: "success", data: { id: 1, name: "John Doe" }, }; if (response.status === "success") { // Handle successful response console.log("User:", response.data.name); } else { // Handle error response console.error("Error occurred:", response.data); } ``` Above, we define an *API response* type with two properties: a data property that can be an object with the properties id and name and a status property that can be either *success* or *error*. In spite of variations in data structure, this guarantees that we manage API answers securely and efficiently. ## Understanding dynamic zones in Strapi In this section, we take a tour through the Strapi framework's dynamic zones feature, which are essential elements for handling changeable content structures and give developers a range of options when it comes to structuring and displaying material. This is made quite clear on the strapi website's front page. “Manage Any Content Anywhere. The leading open-source headless CMS. 100% JavaScript / TypeScript and fully customizable.” ![Strapi-Motto: Manage Any Content Anywhere](https://hackmd.io/_uploads/HknnbmNJR.png) ### Step 1. To get started, create a new Strapi project. The simplest method to begin a new Strapi project is to install it using the Strapi Command Line Interface (CLI). There are other ways to get started as well, such as using the starting CLI. ``` 1. yarn create strapi-app Strapi-TypeScript-App --quickstart 2. Or 3. npx create-strapi-app Strapi-TypeScript-App --quickstart ``` ![Strapi Info](https://hackmd.io/_uploads/By-hSBVyR.png) The code snippets above will create a Strapi-TypeScript-App Folder with all dependencies in it, by default, a server should be running but if it’s not serving, change the directory using your terminal into the Strapi-TypeScript-App folder via “cd Strapi-TypeScript-App\”. You should see similar images like the ones below if the server runs by default, as it is supposed to: ![Strapi server is loading on the terminal.](https://hackmd.io/_uploads/HkzAZcEkR.png) ![Strapi server is loading on the terminal.](https://hackmd.io/_uploads/HklxfcEyR.png) ![Strapi server is loading on the terminal.](https://hackmd.io/_uploads/B18xMqV1R.png) ### Step 2. But if it doesn’t, you can start the admin panel using the commands below, and it should be done inside the directory of Strapi in our case: ![How to manually turn on strapi server](https://hackmd.io/_uploads/rJe2csJZMC.png) ``` npm run develop # OR yarn run develop ``` This ought to open the address in a new browser window. http://localhost:1337/admin/auth/register-admin You can register as an administrator so you will be able to access the dashboard, ensure to fill out the form with accurate details ![Strapi admin registration page](https://hackmd.io/_uploads/SkGMf9EyC.png) ### Defining Dynamic Zones Essentially, dynamic zones are areas of the Strapi interface that are configured so that developers can carefully select and organize different types of material in a dynamic manner. These zones give content producers a blank canvas on which to dynamically establish the composition and organization of their work, allowing them to easily modify and develop their content strategies. Practically speaking, dynamic zones enable developers to design adaptable content models that support a variety of content kinds, from straightforward text fields to intricate media galleries and interactive elements. Dynamic zones give content creators the ability to create dynamic and engaging user experiences that are customized to meet their unique requirements by providing a modular and flexible approach to content creation. Let's see how dynamic zones are created in the Strapi admin interface. Developers can use the Content Types Builder by entering into the Strapi dashboard and choosing the appropriate content type, like "Article" for a news website or "Property" for a real estate platform. Developers can create dynamic zones by adding recurring elements like rich text editors, text fields, and image galleries inside the content type settings. With the help of these dynamic zones, content producers can dynamically modify the arrangement of every article or real estate listing, guaranteeing a personalized and intuitive content management interface. Strapi Dashboard: ![Strapi dashboard ](https://hackmd.io/_uploads/rkLQf9VyA.png) Content Type Builder: ![Strapi dashboard: Content-Type Builder](https://hackmd.io/_uploads/rklgv_nxC.png) ### Role in Handling Changeable Content Frameworks It is impossible to exaggerate the importance of dynamic zones in handling changeable content structures. These zones are the foundation of Strapi's content management system, offering a strong framework for dynamically arranging and presenting content. Dynamic zones give content management operations unmatched flexibility by enabling developers to design and customize content structures on the fly. Dynamic zones provide developers with the resources they need to bring their ideas to life, whether they're organizing a blog post with movable text, photo, and video parts or making a product page with dynamic pricing and specifications. ### Advantages and possible complexities Although dynamic zones have many benefits, such as scalability, flexibility, and reusability, there are certain challenges that developers must overcome. Developers may find it difficult to maintain a large number of dynamic zones, maintain consistency across various content parts, and maximize performance. However, developers can fully utilize dynamic zones in their content management workflows by comprehending these intricacies and putting best practices into effect. With Strapi, developers can unleash new possibilities in content management through dynamic zones that streamline content creation processes and create tailored user experiences. ## Using TypeScript types for Strapi response typing It is important that we comprehend the structure of the data supplied by Strapi before we begin entering our responses. Typically, Strapi provides JSON objects that represent several kinds of content, like user profiles, products, and articles. We are able to determine the attributes and the corresponding data types by examining the response structure. We will have to create an API endpoint using strapi content-type builder. ### Step 1. We will navigate to strapi dashboard and click on *content-type builder*, after that we will create a new *collection type* which will be named *product*. ![Creation of a collection type called product.](https://hackmd.io/_uploads/ByITpkWzR.png) We will then ensure to disable the *Draft & Publish* checkbox by clicking the *ADVANCED SETTINGS*, and then unchecking the *Draft & Publish* as you have seen in the image below. ![Configurations advanced settings](https://hackmd.io/_uploads/HkFDRJWfA.png) Click continue and *Select a field for your collection* in our case we will use *Text* three times. ![Selection field image](https://hackmd.io/_uploads/Byfsyg-GA.png) Click on *Text* and fill in *Author* and click *Finish* repeat the process so you can create *content* and *title* field. ![Add new text field image](https://hackmd.io/_uploads/HyrDxl-f0.png) After you have done that navigate to *Content Manager* in your strapi dashboard so we can populate the content type we just created with data, you should see a dashboard similar to the one below. ![Content Manager: creating a data entry for the product collection type.](https://hackmd.io/_uploads/B1utWgbG0.png) Click the button *Create new entry* after you do that repeat the process to create more entries. ![Entry made to the collection type](https://hackmd.io/_uploads/SJqXGgZzA.png) After creating so many entries and populating your dashboard with data, which is stored in the Strapi SQLite database, you don't need to worry about that. ![Display of the total entries](https://hackmd.io/_uploads/BJ6-XxWzR.png) We will then have to make sure we can access this data from the API endpoint we just created. Navigate to the section called *Settings* and then move down to the section called *USERS & PERMISSIONS PLUGINS* click on *roles*, then click on *public* then click on *product* then check the boxes *find* and *find one* and finally click save, now our API endpoint is ready. ![Settings/Roles/Products](https://hackmd.io/_uploads/SJFFVg-MR.png) If you followed the tutorial actively, you should be able to access your data from this URL endpoint *http://localhost:1337/api/products*. ![Strapi api endpoint with data on the screen image](https://hackmd.io/_uploads/HJkJUx-GC.png) ### Defining TypeScript interfaces We can define TypeScript interfaces to describe our data after we have a firm grasp on the response structure. Interfaces define the types and properties of things, serving as blueprints for them. Let's look at an illustration: To define our TypeScript interface to collect data from our Strapi API end-point, we will navigate to our text editor and create some directories in the root directory where Strapi is located. Our first will be: 1. Datacollections/datafetch.ts 2. funtions/fetchprod.ts 3. interfaces/Products.ts ![Screenshot of this artcile visual studio code workspace](https://hackmd.io/_uploads/HJqLdl-zC.png) ``` //interfaces/Products.ts // Define the TypeScript interface for the Product export interface Product { id: number; title: string; content: string; author: string; createdat: Date; } ``` We will specify the characteristics of Product object, including their corresponding data types, in this interface. These properties include id, title, content, author, and createdAt. ### Typing Strapi Response Functions We will then implement our interfaces in the functions that are in charge of obtaining data from Strapi. We make sure that the provided data complies with the specified interface by typing these routines. Let's examine how we can make this happen: In this section, if you notice we imported *axios* since you are using what we are using for the first time I guess we have to install *axios* module. We will have to open our terminal in the text editor, mine is VS code so I will just press **( ctrl + \` )** Once that is opened just copy and paste the command below, in the root directory, not inside the Strapi, be very careful here, if you are one directory below the root directory you could move up the directory using the **cd ..** command: ![change of directory and install axios library](https://hackmd.io/_uploads/rJxAjxZMC.png) ``` npm install axios ``` ``` //functions/fetchprod.ts import axios from 'axios'; import { Product } from '../interfaces/Products'; const BASE_URL = 'http://localhost:1337'; // Replace with your Strapi base URL export const ProductService = { async fetchProduct(id: number): Promise<Product> { try { const response = await axios.get<Product>(`${BASE_URL}/api/products/${id}`); return response.data; } catch (error) { console.error('Error fetching product:', error); throw error; } } }; ``` The *fetchprod* function is used in this example to retrieve product specifications from Strapi API and to specify that the returned data must comply with the product interface earlier established. ### Handling optional properties Occasionally, some of the properties in our answer might be optional. The '?' syntax in TypeScript enables us to construct optional properties. Let's add optional properties to our interface by updating it: ``` export interface Product { id: number; title: string; content: string; author: string; createdat: Date; updatedat?: Date; } ``` The *updatedat* attribute is indicated as optional in this instance, meaning that the answer may or may not contain it To ensure that our endpoint works properly we will write a script to fetch the data from our terminal. ``` // Datacollection/datafetch.ts import { ProductService } from '../functions/fetchprod'; // Adjust the import path as needed async function testFetchProduct() { try { const productId = 3; // Replace with the ID of the product you want to fetch const product = await ProductService.fetchProduct(productId); console.log('Product:', product); } catch (error) { console.error('Error:', error); } } testFetchProduct(); ``` We have to always take note of the *productId* and the *id* number on our strapi product collection type, if the *id* you are trying to fetch doesn't exist, it will throw an error. So if we eventually delete some entries from our strapi product collection type we have to update the script too. We always ensure that our Strapi server is running before trying to fetch data from our APi endpoint. ![Fetched data on the terminal from strapi api endpoint](https://hackmd.io/_uploads/SyFM4EMM0.png) ### Facilitating seamless integration with React We enable smooth interaction with React applications that leverage Strapi by specifying TypeScript interfaces for Strapi answers. Typed replies reduce the chance of runtime errors and increase developer efficiency by providing IntelliSense help and type-checking. In the next examples, we will delve deeper into sophisticated type strategies and recommended practices for using TypeScript to handle Strapi answers. ## Handling unknown components with TypeScript unions It is rather typical in web development, particularly in content management systems (CMS) like Strapi, to deal with dynamic content whose component types are not preset. TypeScript unions offer a strong way to build adaptable data models that support various content data models in these kinds of situations. ### Understanding TypeScript unions Using a TypeScript union type, we can represent a value that can be one of several different types. The *|* operator is used to specify it in between the types. As an illustration: ``` type MyType = string | number | boolean; ``` Here, *MyType* can be either a string, a number, or a boolean. ### Relevance in dynamic content scenarios Content data models in applications driven by CMS can differ widely. A blog post, for example, could have several data models, such as text, pictures, videos, or even unique elements that the user has created. It is imperative to manage these different data models' kinds dynamically while fetching and rendering such dynamic content. TypeScript unions offer a sophisticated way around this. ### Real-world examples Let's look at a straightforward illustration of content rendering in a blog post. A TypeScript interface can be used to represent each component, and the post's content can be expressed as a union of these interfaces. ``` // interfaces/ProductComponents.ts export interface ProductComponent { id: number; title: string; content: string; createdBy: string; image: string | null; } ``` Now you can iterate over the data models and handle each type appropriately when producing the content of a blog post: ``` // typescriptUnionDynamicContentHandling/populateDynamicArrays.ts import axios from "axios"; import { ProductComponent } from "../interfaces/ProductComponents"; const BASE_URL = "http://localhost:1337"; async function fetchProductComponents(): Promise<ProductComponent[]> { try { const response = await axios.get<any>(`${BASE_URL}/api/products?populate=*`); const products = response.data.data; const components: ProductComponent[] = products.map((product: any, index: number) => { const image = product.attributes.images?.data?.[0]?.attributes.url || null; return { id: index + 1, title: product.attributes.Title, content: product.attributes.content, createdBy: product.attributes.Author, image: image }; }); return components; } catch (error) { console.error('Error fetching product components:', error); throw error; } } // Usage fetchProductComponents().then((components) => { console.log(components); }).catch((error) => { console.error(error); }); ``` Ensure your Strapi server is running locally before proceeding with this article. To run the script above navigate the directory *typescriptUnionDynamicContentHandling/*, the command below. ``` cd typescriptUnionDynamicContentHandling/ ``` You may need to change the above depending on where you are in your directory on your terminal. Then use this to run the script above: ``` ts-node .\populateDynamicInArray.ts ``` You should have a result like the one below. ![Fetched data from strap api on terminal with typescript unions used stored in an array.](https://hackmd.io/_uploads/S1uRGYUMC.png) ### Benefits of TypeScript unions 1. Type Safety: TypeScript unions ensure type safety, preventing unexpected data types from slipping into your code. 2. Flexibility: They allow you to handle dynamic content with ease, accommodating various component types without boilerplate code. 3. Readability: By explicitly defining possible component types, your code becomes more readable and self-explanatory. As we leverage TypeScript unions, you can build robust and flexible applications that seamlessly handle dynamic content in CMS-driven environments. ## Conclusion The significance of TypeScript types and unions for dynamic content management in Strapi-React projects has been discussed in this article. For scalable systems, TypeScript provides a strong typing system that improves code readability and guarantees type safety. Understanding TypeScript types, using unions to handle dynamic content, and practical examples showing how to apply them in flexible data structures are some of the key ideas presented. In today's web development, dynamic content management is essential, and TypeScript unions and types allow for efficient administration without sacrificing code clarity or type safety. The significance of type safety, adaptability using unions, and readability through unambiguous interfaces are among the important lessons learned. Clear interface definition, working with unions to handle unknown data models, and promoting communication and cooperation are all examples of best practices. Developers can create more durable and manageable apps for efficiently handling dynamic content by utilizing TypeScript types and unions. Stay curious, keep coding, and have fun creating! ## Additional Resources 1. [Complete Github Repository](https://github.com/Alpheus-godswill1/Dynamic-Content-Handling-with-TypeScript-and-Strapi.git) 2. [Axios Documentation](https://axios-http.com/docs/intro) 3. [TypeScript Interface Documentation](https://www.typescriptlang.org/docs/handbook/typescript-tooling-in-5-minutes.html#interfaces) 4. [Strapi Quick Start Guide](https://docs.strapi.io/dev-docs/quick-start)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully