Let’s look at a simple example that shows an error message when an error occurs.
<aura:if isTrue="{!v.isError}"> <div>{!v.errorMessage}</div> </aura:if>
The <div> component and its contents are only created and rendered if the value of the isTrue expression evaluates to true. If the value of the isTrue expression changes and evaluates to false, all the components inside the <aura:if> tag are destroyed. The components are created again if the isTrue expression changes again and evaluates to true.
The general guideline is to use <aura:if> because it helps your components load faster initially by deferring the creation and rendering of the enclosed element tree until the condition is fulfilled.
You can use CSS to toggle visibility of markup by calling $A.util.toggleClass(cmp, 'class') in JavaScript code.
Elements in markup are created and rendered up front, but they’re hidden. For an example, see Dynamically Showing or Hiding Markup.
The conditional markup is created and rendered even if it’s not used, so <aura:if> is preferred.
You can dynamically create components in JavaScript code. However, writing code is usually harder to maintain and debug than using markup. Again, using <aura:if> is preferred but the best design choice depends on your use case.